rubocop-rails 2.6.0 → 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 +189 -6
- data/lib/rubocop/cop/mixin/active_record_helper.rb +12 -3
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +40 -0
- data/lib/rubocop/cop/mixin/index_method.rb +25 -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 +148 -0
- 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 +91 -0
- 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 +20 -33
- 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 +61 -0
- data/lib/rubocop/cop/rails/delegate.rb +10 -10
- data/lib/rubocop/cop/rails/delegate_allow_blank.rb +7 -8
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +13 -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 +5 -4
- data/lib/rubocop/cop/rails/find_by.rb +13 -13
- data/lib/rubocop/cop/rails/find_by_id.rb +94 -0
- 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 +4 -7
- data/lib/rubocop/cop/rails/helper_instance_variable.rb +4 -2
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +25 -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 +11 -2
- data/lib/rubocop/cop/rails/index_with.rb +11 -2
- data/lib/rubocop/cop/rails/inquiry.rb +39 -0
- 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 -20
- data/lib/rubocop/cop/rails/mailer_name.rb +86 -0
- data/lib/rubocop/cop/rails/match_route.rb +120 -0
- data/lib/rubocop/cop/rails/negate_include.rb +41 -0
- data/lib/rubocop/cop/rails/not_null_column.rb +2 -1
- data/lib/rubocop/cop/rails/order_by_id.rb +52 -0
- 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 +21 -15
- data/lib/rubocop/cop/rails/pluck.rb +56 -0
- data/lib/rubocop/cop/rails/pluck_id.rb +56 -0
- data/lib/rubocop/cop/rails/pluck_in_where.rb +70 -0
- 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 +9 -11
- 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 +4 -3
- data/lib/rubocop/cop/rails/refute_methods.rb +9 -10
- data/lib/rubocop/cop/rails/relative_date_constant.rb +20 -9
- data/lib/rubocop/cop/rails/render_inline.rb +41 -0
- data/lib/rubocop/cop/rails/render_plain_text.rb +71 -0
- data/lib/rubocop/cop/rails/request_referer.rb +7 -7
- data/lib/rubocop/cop/rails/reversible_migration.rb +82 -7
- data/lib/rubocop/cop/rails/safe_navigation.rb +12 -11
- data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +5 -10
- data/lib/rubocop/cop/rails/save_bang.rb +19 -22
- data/lib/rubocop/cop/rails/scope_args.rb +2 -1
- data/lib/rubocop/cop/rails/short_i18n.rb +74 -0
- data/lib/rubocop/cop/rails/skips_model_validations.rb +46 -11
- data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +82 -0
- data/lib/rubocop/cop/rails/time_zone.rb +22 -20
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +10 -10
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +18 -8
- data/lib/rubocop/cop/rails/unknown_env.rb +15 -4
- 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 +126 -0
- data/lib/rubocop/cop/rails/where_not.rb +97 -0
- data/lib/rubocop/cop/rails_cops.rb +22 -0
- data/lib/rubocop/rails/schema_loader.rb +4 -4
- data/lib/rubocop/rails/schema_loader/schema.rb +5 -5
- data/lib/rubocop/rails/version.rb +5 -1
- metadata +37 -9
@@ -24,10 +24,12 @@ module RuboCop
|
|
24
24
|
# class Comment
|
25
25
|
# belongs_to :author, foreign_key: 'user_id'
|
26
26
|
# end
|
27
|
-
class RedundantForeignKey <
|
27
|
+
class RedundantForeignKey < Base
|
28
28
|
include RangeHelp
|
29
|
+
extend AutoCorrector
|
29
30
|
|
30
31
|
MSG = 'Specifying the default value for `foreign_key` is redundant.'
|
32
|
+
RESTRICT_ON_SEND = %i[belongs_to has_one has_many has_and_belongs_to_many].freeze
|
31
33
|
|
32
34
|
def_node_matcher :association_with_foreign_key, <<~PATTERN
|
33
35
|
(send nil? ${:belongs_to :has_one :has_many :has_and_belongs_to_many} ({sym str} $_)
|
@@ -38,18 +40,13 @@ module RuboCop
|
|
38
40
|
def on_send(node)
|
39
41
|
association_with_foreign_key(node) do |type, name, options, foreign_key_pair, foreign_key|
|
40
42
|
if redundant?(node, type, name, options, foreign_key)
|
41
|
-
add_offense(
|
42
|
-
|
43
|
-
|
44
|
-
end
|
43
|
+
add_offense(foreign_key_pair.loc.expression) do |corrector|
|
44
|
+
range = range_with_surrounding_space(range: foreign_key_pair.source_range, side: :left)
|
45
|
+
range = range_with_surrounding_comma(range, :left)
|
45
46
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
range = range_with_surrounding_comma(range, :left)
|
50
|
-
|
51
|
-
lambda do |corrector|
|
52
|
-
corrector.remove(range)
|
47
|
+
corrector.remove(range)
|
48
|
+
end
|
49
|
+
end
|
53
50
|
end
|
54
51
|
end
|
55
52
|
|
@@ -54,8 +54,9 @@ module RuboCop
|
|
54
54
|
# merger.invoke(another_receiver)
|
55
55
|
# end
|
56
56
|
# end
|
57
|
-
class RedundantReceiverInWithOptions <
|
57
|
+
class RedundantReceiverInWithOptions < Base
|
58
58
|
include RangeHelp
|
59
|
+
extend AutoCorrector
|
59
60
|
|
60
61
|
MSG = 'Redundant receiver in `with_options`.'
|
61
62
|
|
@@ -86,22 +87,22 @@ module RuboCop
|
|
86
87
|
if send_nodes.all? { |n| same_value?(arg, n.receiver) }
|
87
88
|
send_nodes.each do |send_node|
|
88
89
|
receiver = send_node.receiver
|
89
|
-
add_offense(
|
90
|
+
add_offense(receiver.source_range) do |corrector|
|
91
|
+
autocorrect(corrector, send_node)
|
92
|
+
end
|
90
93
|
end
|
91
94
|
end
|
92
95
|
end
|
93
96
|
end
|
94
97
|
|
95
|
-
def autocorrect(node)
|
96
|
-
lambda do |corrector|
|
97
|
-
corrector.remove(node.receiver.source_range)
|
98
|
-
corrector.remove(node.loc.dot)
|
99
|
-
corrector.remove(block_argument_range(node))
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
98
|
private
|
104
99
|
|
100
|
+
def autocorrect(corrector, node)
|
101
|
+
corrector.remove(node.receiver.source_range)
|
102
|
+
corrector.remove(node.loc.dot)
|
103
|
+
corrector.remove(block_argument_range(node))
|
104
|
+
end
|
105
|
+
|
105
106
|
def block_argument_range(node)
|
106
107
|
block_node = node.each_ancestor(:block).first
|
107
108
|
block_argument = block_node.children[1].source_range
|
@@ -13,11 +13,12 @@ module RuboCop
|
|
13
13
|
#
|
14
14
|
# # good
|
15
15
|
# has_many :accounts, class_name: 'Account'
|
16
|
-
class ReflectionClassName <
|
16
|
+
class ReflectionClassName < Base
|
17
17
|
MSG = 'Use a string value for `class_name`.'
|
18
|
+
RESTRICT_ON_SEND = %i[has_many has_one belongs_to].freeze
|
18
19
|
|
19
20
|
def_node_matcher :association_with_reflection, <<~PATTERN
|
20
|
-
(send nil? {:has_many :has_one :belongs_to} _
|
21
|
+
(send nil? {:has_many :has_one :belongs_to} _ _ ?
|
21
22
|
(hash <$#reflection_class_name ...>)
|
22
23
|
)
|
23
24
|
PATTERN
|
@@ -28,7 +29,7 @@ module RuboCop
|
|
28
29
|
|
29
30
|
def on_send(node)
|
30
31
|
association_with_reflection(node) do |reflection_class_name|
|
31
|
-
add_offense(
|
32
|
+
add_offense(reflection_class_name.loc.expression)
|
32
33
|
end
|
33
34
|
end
|
34
35
|
end
|
@@ -28,8 +28,9 @@ module RuboCop
|
|
28
28
|
# refute_empty [1, 2, 3]
|
29
29
|
# refute_equal true, false
|
30
30
|
#
|
31
|
-
class RefuteMethods <
|
31
|
+
class RefuteMethods < Base
|
32
32
|
include ConfigurableEnforcedStyle
|
33
|
+
extend AutoCorrector
|
33
34
|
|
34
35
|
MSG = 'Prefer `%<good_method>s` over `%<bad_method>s`.'
|
35
36
|
|
@@ -53,21 +54,19 @@ module RuboCop
|
|
53
54
|
REFUTE_METHODS = CORRECTIONS.keys.freeze
|
54
55
|
ASSERT_NOT_METHODS = CORRECTIONS.values.freeze
|
55
56
|
|
57
|
+
RESTRICT_ON_SEND = REFUTE_METHODS + ASSERT_NOT_METHODS
|
58
|
+
|
56
59
|
def_node_matcher :offensive?, '(send nil? #bad_method? ...)'
|
57
60
|
|
58
61
|
def on_send(node)
|
59
62
|
return unless offensive?(node)
|
60
63
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
def autocorrect(node)
|
66
|
-
bad_method = node.method_name
|
67
|
-
good_method = convert_good_method(bad_method)
|
64
|
+
method_name = node.method_name
|
65
|
+
message = offense_message(method_name)
|
66
|
+
range = node.loc.selector
|
68
67
|
|
69
|
-
|
70
|
-
corrector.replace(
|
68
|
+
add_offense(range, message: message) do |corrector|
|
69
|
+
corrector.replace(range, convert_good_method(method_name))
|
71
70
|
end
|
72
71
|
end
|
73
72
|
|
@@ -27,15 +27,18 @@ module RuboCop
|
|
27
27
|
# 1.week.since
|
28
28
|
# end
|
29
29
|
# end
|
30
|
-
class RelativeDateConstant <
|
30
|
+
class RelativeDateConstant < Base
|
31
31
|
include RangeHelp
|
32
|
+
extend AutoCorrector
|
32
33
|
|
33
34
|
MSG = 'Do not assign %<method_name>s to constants as it ' \
|
34
35
|
'will be evaluated only once.'
|
35
36
|
|
36
37
|
def on_casgn(node)
|
37
38
|
relative_date_assignment?(node) do |method_name|
|
38
|
-
add_offense(node, message:
|
39
|
+
add_offense(node, message: message(method_name)) do |corrector|
|
40
|
+
autocorrect(corrector, node)
|
41
|
+
end
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
@@ -48,10 +51,9 @@ module RuboCop
|
|
48
51
|
next unless name.casgn_type?
|
49
52
|
|
50
53
|
relative_date?(value) do |method_name|
|
51
|
-
add_offense(
|
52
|
-
|
53
|
-
|
54
|
-
message: format(MSG, method_name: method_name))
|
54
|
+
add_offense(offense_range(name, value), message: message(method_name)) do |corrector|
|
55
|
+
autocorrect(corrector, node)
|
56
|
+
end
|
55
57
|
end
|
56
58
|
end
|
57
59
|
end
|
@@ -62,7 +64,9 @@ module RuboCop
|
|
62
64
|
end
|
63
65
|
end
|
64
66
|
|
65
|
-
|
67
|
+
private
|
68
|
+
|
69
|
+
def autocorrect(corrector, node)
|
66
70
|
return unless node.casgn_type?
|
67
71
|
|
68
72
|
scope, const_name, value = *node
|
@@ -72,10 +76,17 @@ module RuboCop
|
|
72
76
|
new_code = ["def self.#{const_name.downcase}",
|
73
77
|
"#{indent}#{value.source}",
|
74
78
|
'end'].join("\n#{indent}")
|
75
|
-
|
79
|
+
|
80
|
+
corrector.replace(node.source_range, new_code)
|
76
81
|
end
|
77
82
|
|
78
|
-
|
83
|
+
def message(method_name)
|
84
|
+
format(MSG, method_name: method_name)
|
85
|
+
end
|
86
|
+
|
87
|
+
def offense_range(name, value)
|
88
|
+
range_between(name.loc.expression.begin_pos, value.loc.expression.end_pos)
|
89
|
+
end
|
79
90
|
|
80
91
|
def_node_matcher :relative_date_assignment?, <<~PATTERN
|
81
92
|
{
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop looks for inline rendering within controller actions.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# class ProductsController < ApplicationController
|
11
|
+
# def index
|
12
|
+
# render inline: "<% products.each do |p| %><p><%= p.name %></p><% end %>", type: :erb
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# # app/views/products/index.html.erb
|
18
|
+
# # <% products.each do |p| %>
|
19
|
+
# # <p><%= p.name %></p>
|
20
|
+
# # <% end %>
|
21
|
+
#
|
22
|
+
# class ProductsController < ApplicationController
|
23
|
+
# def index
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
class RenderInline < Base
|
28
|
+
MSG = 'Prefer using a template over inline rendering.'
|
29
|
+
RESTRICT_ON_SEND = %i[render].freeze
|
30
|
+
|
31
|
+
def_node_matcher :render_with_inline_option?, <<~PATTERN
|
32
|
+
(send nil? :render (hash <(pair {(sym :inline) (str "inline")} _) ...>))
|
33
|
+
PATTERN
|
34
|
+
|
35
|
+
def on_send(node)
|
36
|
+
add_offense(node) if render_with_inline_option?(node)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop identifies places where `render text:` can be
|
7
|
+
# replaced with `render plain:`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad - explicit MIME type to `text/plain`
|
11
|
+
# render text: 'Ruby!', content_type: 'text/plain'
|
12
|
+
#
|
13
|
+
# # good - short and precise
|
14
|
+
# render plain: 'Ruby!'
|
15
|
+
#
|
16
|
+
# # good - explicit MIME type not to `text/plain`
|
17
|
+
# render text: 'Ruby!', content_type: 'text/html'
|
18
|
+
#
|
19
|
+
# @example ContentTypeCompatibility: true (default)
|
20
|
+
# # good - sets MIME type to `text/html`
|
21
|
+
# render text: 'Ruby!'
|
22
|
+
#
|
23
|
+
# @example ContentTypeCompatibility: false
|
24
|
+
# # bad - sets MIME type to `text/html`
|
25
|
+
# render text: 'Ruby!'
|
26
|
+
#
|
27
|
+
class RenderPlainText < Base
|
28
|
+
extend AutoCorrector
|
29
|
+
|
30
|
+
MSG = 'Prefer `render plain:` over `render text:`.'
|
31
|
+
RESTRICT_ON_SEND = %i[render].freeze
|
32
|
+
|
33
|
+
def_node_matcher :render_plain_text?, <<~PATTERN
|
34
|
+
(send nil? :render $(hash <$(pair (sym :text) $_) ...>))
|
35
|
+
PATTERN
|
36
|
+
|
37
|
+
def on_send(node)
|
38
|
+
render_plain_text?(node) do |options_node, option_node, option_value|
|
39
|
+
content_type_node = find_content_type(options_node)
|
40
|
+
return unless compatible_content_type?(content_type_node)
|
41
|
+
|
42
|
+
add_offense(node) do |corrector|
|
43
|
+
rest_options = options_node.pairs - [option_node, content_type_node].compact
|
44
|
+
|
45
|
+
corrector.replace(node, replacement(rest_options, option_value))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def find_content_type(node)
|
53
|
+
node.pairs.find { |p| p.key.value.to_sym == :content_type }
|
54
|
+
end
|
55
|
+
|
56
|
+
def compatible_content_type?(node)
|
57
|
+
(node && node.value.value == 'text/plain') ||
|
58
|
+
(!node && !cop_config['ContentTypeCompatibility'])
|
59
|
+
end
|
60
|
+
|
61
|
+
def replacement(rest_options, option_value)
|
62
|
+
if rest_options.any?
|
63
|
+
"render plain: #{option_value.source}, #{rest_options.map(&:source).join(', ')}"
|
64
|
+
else
|
65
|
+
"render plain: #{option_value.source}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -19,11 +19,13 @@ module RuboCop
|
|
19
19
|
#
|
20
20
|
# # good
|
21
21
|
# request.referrer
|
22
|
-
class RequestReferer <
|
22
|
+
class RequestReferer < Base
|
23
23
|
include ConfigurableEnforcedStyle
|
24
|
+
extend AutoCorrector
|
24
25
|
|
25
26
|
MSG = 'Use `request.%<prefer>s` instead of ' \
|
26
27
|
'`request.%<current>s`.'
|
28
|
+
RESTRICT_ON_SEND = %i[referer referrer].freeze
|
27
29
|
|
28
30
|
def_node_matcher :referer?, <<~PATTERN
|
29
31
|
(send (send nil? :request) {:referer :referrer})
|
@@ -33,17 +35,15 @@ module RuboCop
|
|
33
35
|
referer?(node) do
|
34
36
|
return unless node.method?(wrong_method_name)
|
35
37
|
|
36
|
-
add_offense(node.source_range
|
38
|
+
add_offense(node.source_range) do |corrector|
|
39
|
+
corrector.replace(node, "request.#{style}")
|
40
|
+
end
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
40
|
-
def autocorrect(node)
|
41
|
-
->(corrector) { corrector.replace(node, "request.#{style}") }
|
42
|
-
end
|
43
|
-
|
44
44
|
private
|
45
45
|
|
46
|
-
def message(
|
46
|
+
def message(_range)
|
47
47
|
format(MSG, prefer: style, current: wrong_method_name)
|
48
48
|
end
|
49
49
|
|
@@ -129,8 +129,53 @@ module RuboCop
|
|
129
129
|
# end
|
130
130
|
# end
|
131
131
|
#
|
132
|
+
# @example
|
133
|
+
# # remove_columns
|
134
|
+
#
|
135
|
+
# # bad
|
136
|
+
# def change
|
137
|
+
# remove_columns :users, :name, :email
|
138
|
+
# end
|
139
|
+
#
|
140
|
+
# # good
|
141
|
+
# def change
|
142
|
+
# reversible do |dir|
|
143
|
+
# dir.up do
|
144
|
+
# remove_columns :users, :name, :email
|
145
|
+
# end
|
146
|
+
#
|
147
|
+
# dir.down do
|
148
|
+
# add_column :users, :name, :string
|
149
|
+
# add_column :users, :email, :string
|
150
|
+
# end
|
151
|
+
# end
|
152
|
+
# end
|
153
|
+
#
|
154
|
+
# # good (Rails >= 6.1, see https://github.com/rails/rails/pull/36589)
|
155
|
+
# def change
|
156
|
+
# remove_columns :users, :name, :email, type: :string
|
157
|
+
# end
|
158
|
+
#
|
159
|
+
# @example
|
160
|
+
# # remove_index
|
161
|
+
#
|
162
|
+
# # bad
|
163
|
+
# def change
|
164
|
+
# remove_index :users, name: :index_users_on_email
|
165
|
+
# end
|
166
|
+
#
|
167
|
+
# # good
|
168
|
+
# def change
|
169
|
+
# remove_index :users, :email
|
170
|
+
# end
|
171
|
+
#
|
172
|
+
# # good
|
173
|
+
# def change
|
174
|
+
# remove_index :users, column: :email
|
175
|
+
# end
|
176
|
+
#
|
132
177
|
# @see https://api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html
|
133
|
-
class ReversibleMigration <
|
178
|
+
class ReversibleMigration < Base
|
134
179
|
MSG = '%<action>s is not reversible.'
|
135
180
|
|
136
181
|
def_node_matcher :irreversible_schema_statement_call, <<~PATTERN
|
@@ -153,6 +198,14 @@ module RuboCop
|
|
153
198
|
(send nil? :change_table $_ ...)
|
154
199
|
PATTERN
|
155
200
|
|
201
|
+
def_node_matcher :remove_columns_call, <<~PATTERN
|
202
|
+
(send nil? :remove_columns ... $_)
|
203
|
+
PATTERN
|
204
|
+
|
205
|
+
def_node_matcher :remove_index_call, <<~PATTERN
|
206
|
+
(send nil? :remove_index _ $_)
|
207
|
+
PATTERN
|
208
|
+
|
156
209
|
def on_send(node)
|
157
210
|
return unless within_change_method?(node)
|
158
211
|
return if within_reversible_or_up_only_block?(node)
|
@@ -162,6 +215,8 @@ module RuboCop
|
|
162
215
|
check_reversible_hash_node(node)
|
163
216
|
check_remove_column_node(node)
|
164
217
|
check_remove_foreign_key_node(node)
|
218
|
+
check_remove_columns_node(node)
|
219
|
+
check_remove_index_node(node)
|
165
220
|
end
|
166
221
|
|
167
222
|
def on_block(node)
|
@@ -216,11 +271,7 @@ module RuboCop
|
|
216
271
|
def check_remove_foreign_key_node(node)
|
217
272
|
remove_foreign_key_call(node) do |arg|
|
218
273
|
if arg.hash_type? && !all_hash_key?(arg, :to_table)
|
219
|
-
add_offense(
|
220
|
-
node,
|
221
|
-
message: format(MSG,
|
222
|
-
action: 'remove_foreign_key(without table)')
|
223
|
-
)
|
274
|
+
add_offense(node, message: format(MSG, action: 'remove_foreign_key(without table)'))
|
224
275
|
end
|
225
276
|
end
|
226
277
|
end
|
@@ -237,6 +288,30 @@ module RuboCop
|
|
237
288
|
end
|
238
289
|
end
|
239
290
|
|
291
|
+
def check_remove_columns_node(node)
|
292
|
+
remove_columns_call(node) do |args|
|
293
|
+
unless all_hash_key?(args, :type) && target_rails_version >= 6.1
|
294
|
+
action = target_rails_version >= 6.1 ? 'remove_columns(without type)' : 'remove_columns'
|
295
|
+
|
296
|
+
add_offense(
|
297
|
+
node,
|
298
|
+
message: format(MSG, action: action)
|
299
|
+
)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def check_remove_index_node(node)
|
305
|
+
remove_index_call(node) do |args|
|
306
|
+
if args.hash_type? && !all_hash_key?(args, :column)
|
307
|
+
add_offense(
|
308
|
+
node,
|
309
|
+
message: format(MSG, action: 'remove_index(without column)')
|
310
|
+
)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
240
315
|
def check_change_table_offense(receiver, node)
|
241
316
|
method_name = node.method_name
|
242
317
|
return if receiver != node.receiver &&
|
@@ -281,7 +356,7 @@ module RuboCop
|
|
281
356
|
key.children.first.to_sym
|
282
357
|
end
|
283
358
|
|
284
|
-
hash_keys & keys == keys
|
359
|
+
(hash_keys & keys).sort == keys
|
285
360
|
end
|
286
361
|
end
|
287
362
|
end
|