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.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +16 -0
  3. data/config/default.yml +189 -6
  4. data/lib/rubocop/cop/mixin/active_record_helper.rb +12 -3
  5. data/lib/rubocop/cop/mixin/enforce_superclass.rb +40 -0
  6. data/lib/rubocop/cop/mixin/index_method.rb +25 -11
  7. data/lib/rubocop/cop/rails/action_filter.rb +10 -14
  8. data/lib/rubocop/cop/rails/active_record_aliases.rb +13 -17
  9. data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +148 -0
  10. data/lib/rubocop/cop/rails/active_record_override.rb +1 -1
  11. data/lib/rubocop/cop/rails/active_support_aliases.rb +12 -21
  12. data/lib/rubocop/cop/rails/after_commit_override.rb +91 -0
  13. data/lib/rubocop/cop/rails/application_controller.rb +3 -7
  14. data/lib/rubocop/cop/rails/application_job.rb +2 -1
  15. data/lib/rubocop/cop/rails/application_mailer.rb +2 -7
  16. data/lib/rubocop/cop/rails/application_record.rb +2 -7
  17. data/lib/rubocop/cop/rails/arel_star.rb +41 -0
  18. data/lib/rubocop/cop/rails/assert_not.rb +8 -10
  19. data/lib/rubocop/cop/rails/attribute_default_block_value.rb +90 -0
  20. data/lib/rubocop/cop/rails/belongs_to.rb +9 -18
  21. data/lib/rubocop/cop/rails/blank.rb +27 -27
  22. data/lib/rubocop/cop/rails/bulk_change_table.rb +1 -1
  23. data/lib/rubocop/cop/rails/content_tag.rb +20 -33
  24. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +2 -1
  25. data/lib/rubocop/cop/rails/date.rb +10 -11
  26. data/lib/rubocop/cop/rails/default_scope.rb +61 -0
  27. data/lib/rubocop/cop/rails/delegate.rb +10 -10
  28. data/lib/rubocop/cop/rails/delegate_allow_blank.rb +7 -8
  29. data/lib/rubocop/cop/rails/dynamic_find_by.rb +13 -11
  30. data/lib/rubocop/cop/rails/enum_hash.rb +11 -10
  31. data/lib/rubocop/cop/rails/enum_uniqueness.rb +2 -1
  32. data/lib/rubocop/cop/rails/environment_comparison.rb +18 -14
  33. data/lib/rubocop/cop/rails/exit.rb +4 -10
  34. data/lib/rubocop/cop/rails/file_path.rb +5 -4
  35. data/lib/rubocop/cop/rails/find_by.rb +13 -13
  36. data/lib/rubocop/cop/rails/find_by_id.rb +94 -0
  37. data/lib/rubocop/cop/rails/find_each.rb +16 -14
  38. data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +3 -2
  39. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +4 -7
  40. data/lib/rubocop/cop/rails/helper_instance_variable.rb +4 -2
  41. data/lib/rubocop/cop/rails/http_positional_arguments.rb +25 -21
  42. data/lib/rubocop/cop/rails/http_status.rb +7 -9
  43. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +8 -6
  44. data/lib/rubocop/cop/rails/index_by.rb +11 -2
  45. data/lib/rubocop/cop/rails/index_with.rb +11 -2
  46. data/lib/rubocop/cop/rails/inquiry.rb +39 -0
  47. data/lib/rubocop/cop/rails/inverse_of.rb +3 -2
  48. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +17 -15
  49. data/lib/rubocop/cop/rails/link_to_blank.rb +20 -20
  50. data/lib/rubocop/cop/rails/mailer_name.rb +86 -0
  51. data/lib/rubocop/cop/rails/match_route.rb +120 -0
  52. data/lib/rubocop/cop/rails/negate_include.rb +41 -0
  53. data/lib/rubocop/cop/rails/not_null_column.rb +2 -1
  54. data/lib/rubocop/cop/rails/order_by_id.rb +52 -0
  55. data/lib/rubocop/cop/rails/output.rb +5 -2
  56. data/lib/rubocop/cop/rails/output_safety.rb +3 -2
  57. data/lib/rubocop/cop/rails/pick.rb +21 -15
  58. data/lib/rubocop/cop/rails/pluck.rb +56 -0
  59. data/lib/rubocop/cop/rails/pluck_id.rb +56 -0
  60. data/lib/rubocop/cop/rails/pluck_in_where.rb +70 -0
  61. data/lib/rubocop/cop/rails/pluralization_grammar.rb +10 -14
  62. data/lib/rubocop/cop/rails/presence.rb +12 -13
  63. data/lib/rubocop/cop/rails/present.rb +30 -24
  64. data/lib/rubocop/cop/rails/rake_environment.rb +9 -11
  65. data/lib/rubocop/cop/rails/read_write_attribute.rb +12 -11
  66. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +29 -31
  67. data/lib/rubocop/cop/rails/redundant_foreign_key.rb +9 -12
  68. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +11 -10
  69. data/lib/rubocop/cop/rails/reflection_class_name.rb +4 -3
  70. data/lib/rubocop/cop/rails/refute_methods.rb +9 -10
  71. data/lib/rubocop/cop/rails/relative_date_constant.rb +20 -9
  72. data/lib/rubocop/cop/rails/render_inline.rb +41 -0
  73. data/lib/rubocop/cop/rails/render_plain_text.rb +71 -0
  74. data/lib/rubocop/cop/rails/request_referer.rb +7 -7
  75. data/lib/rubocop/cop/rails/reversible_migration.rb +82 -7
  76. data/lib/rubocop/cop/rails/safe_navigation.rb +12 -11
  77. data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +5 -10
  78. data/lib/rubocop/cop/rails/save_bang.rb +19 -22
  79. data/lib/rubocop/cop/rails/scope_args.rb +2 -1
  80. data/lib/rubocop/cop/rails/short_i18n.rb +74 -0
  81. data/lib/rubocop/cop/rails/skips_model_validations.rb +46 -11
  82. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +82 -0
  83. data/lib/rubocop/cop/rails/time_zone.rb +22 -20
  84. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +10 -10
  85. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +18 -8
  86. data/lib/rubocop/cop/rails/unknown_env.rb +15 -4
  87. data/lib/rubocop/cop/rails/validation.rb +15 -14
  88. data/lib/rubocop/cop/rails/where_equals.rb +94 -0
  89. data/lib/rubocop/cop/rails/where_exists.rb +126 -0
  90. data/lib/rubocop/cop/rails/where_not.rb +97 -0
  91. data/lib/rubocop/cop/rails_cops.rb +22 -0
  92. data/lib/rubocop/rails/schema_loader.rb +4 -4
  93. data/lib/rubocop/rails/schema_loader/schema.rb +5 -5
  94. data/lib/rubocop/rails/version.rb +5 -1
  95. 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 < Cop
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(node, location: foreign_key_pair.loc.expression)
42
- end
43
- end
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
- 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)
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 < Cop
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(send_node, location: receiver.source_range)
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 < Cop
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(node, location: reflection_class_name.loc.expression)
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 < Cop
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
- message = offense_message(node.method_name)
62
- add_offense(node, location: :selector, message: message)
63
- end
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
- lambda do |corrector|
70
- corrector.replace(node.loc.selector, good_method.to_s)
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 < Cop
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: format(MSG, method_name: method_name))
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(node,
52
- location: range_between(name.loc.expression.begin_pos,
53
- value.loc.expression.end_pos),
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
- def autocorrect(node)
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
- ->(corrector) { corrector.replace(node.source_range, new_code) }
79
+
80
+ corrector.replace(node.source_range, new_code)
76
81
  end
77
82
 
78
- private
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 < Cop
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, location: 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(_node)
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 < Cop
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