rubocop-rails 2.19.1 → 2.30.3
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 +70 -16
- data/config/default.yml +173 -28
- data/lib/rubocop/cop/mixin/active_record_helper.rb +16 -4
- data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +2 -2
- data/lib/rubocop/cop/mixin/database_type_resolvable.rb +66 -0
- data/lib/rubocop/cop/mixin/index_method.rb +68 -61
- data/lib/rubocop/cop/mixin/routes_helper.rb +20 -0
- data/lib/rubocop/cop/mixin/target_rails_version.rb +27 -2
- data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +3 -1
- data/lib/rubocop/cop/rails/action_controller_test_case.rb +2 -2
- data/lib/rubocop/cop/rails/action_filter.rb +3 -0
- data/lib/rubocop/cop/rails/action_order.rb +1 -5
- data/lib/rubocop/cop/rails/active_record_aliases.rb +2 -2
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +1 -5
- data/lib/rubocop/cop/rails/active_support_aliases.rb +6 -5
- data/lib/rubocop/cop/rails/active_support_on_load.rb +21 -1
- data/lib/rubocop/cop/rails/add_column_index.rb +1 -0
- data/lib/rubocop/cop/rails/after_commit_override.rb +1 -1
- data/lib/rubocop/cop/rails/application_record.rb +4 -0
- data/lib/rubocop/cop/rails/assert_not.rb +0 -1
- data/lib/rubocop/cop/rails/belongs_to.rb +1 -1
- data/lib/rubocop/cop/rails/blank.rb +1 -1
- data/lib/rubocop/cop/rails/bulk_change_table.rb +19 -45
- data/lib/rubocop/cop/rails/compact_blank.rb +29 -8
- data/lib/rubocop/cop/rails/content_tag.rb +2 -2
- data/lib/rubocop/cop/rails/dangerous_column_names.rb +448 -0
- data/lib/rubocop/cop/rails/date.rb +14 -5
- data/lib/rubocop/cop/rails/delegate.rb +53 -7
- data/lib/rubocop/cop/rails/duplicate_association.rb +71 -10
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +3 -3
- data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +2 -2
- data/lib/rubocop/cop/rails/enum_hash.rb +31 -8
- data/lib/rubocop/cop/rails/enum_syntax.rb +130 -0
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +29 -7
- data/lib/rubocop/cop/rails/env_local.rb +69 -0
- data/lib/rubocop/cop/rails/expanded_date_range.rb +1 -1
- data/lib/rubocop/cop/rails/file_path.rb +186 -18
- data/lib/rubocop/cop/rails/find_by.rb +3 -3
- data/lib/rubocop/cop/rails/find_by_id.rb +9 -23
- data/lib/rubocop/cop/rails/find_each.rb +1 -1
- data/lib/rubocop/cop/rails/freeze_time.rb +1 -1
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +1 -1
- data/lib/rubocop/cop/rails/helper_instance_variable.rb +1 -1
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +7 -0
- data/lib/rubocop/cop/rails/http_status.rb +16 -5
- data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +63 -13
- data/lib/rubocop/cop/rails/i18n_locale_texts.rb +5 -1
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +23 -3
- data/lib/rubocop/cop/rails/index_by.rb +28 -12
- data/lib/rubocop/cop/rails/index_with.rb +28 -12
- data/lib/rubocop/cop/rails/inquiry.rb +2 -1
- data/lib/rubocop/cop/rails/inverse_of.rb +1 -1
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +19 -10
- data/lib/rubocop/cop/rails/link_to_blank.rb +2 -2
- data/lib/rubocop/cop/rails/match_route.rb +1 -9
- data/lib/rubocop/cop/rails/multiple_route_paths.rb +50 -0
- data/lib/rubocop/cop/rails/not_null_column.rb +100 -6
- data/lib/rubocop/cop/rails/output.rb +3 -2
- data/lib/rubocop/cop/rails/pick.rb +10 -5
- data/lib/rubocop/cop/rails/pluck.rb +21 -1
- data/lib/rubocop/cop/rails/pluck_id.rb +2 -1
- data/lib/rubocop/cop/rails/pluck_in_where.rb +35 -13
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +30 -16
- data/lib/rubocop/cop/rails/presence.rb +1 -1
- data/lib/rubocop/cop/rails/present.rb +1 -3
- data/lib/rubocop/cop/rails/rake_environment.rb +22 -6
- data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +190 -0
- data/lib/rubocop/cop/rails/redundant_foreign_key.rb +1 -1
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +16 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +2 -2
- data/lib/rubocop/cop/rails/reflection_class_name.rb +2 -2
- data/lib/rubocop/cop/rails/refute_methods.rb +0 -1
- data/lib/rubocop/cop/rails/relative_date_constant.rb +1 -1
- data/lib/rubocop/cop/rails/render_plain_text.rb +6 -3
- data/lib/rubocop/cop/rails/request_referer.rb +1 -1
- data/lib/rubocop/cop/rails/response_parsed_body.rb +52 -10
- data/lib/rubocop/cop/rails/reversible_migration.rb +7 -5
- data/lib/rubocop/cop/rails/root_pathname_methods.rb +58 -15
- data/lib/rubocop/cop/rails/save_bang.rb +22 -14
- data/lib/rubocop/cop/rails/schema_comment.rb +17 -10
- data/lib/rubocop/cop/rails/select_map.rb +79 -0
- data/lib/rubocop/cop/rails/skips_model_validations.rb +9 -4
- data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +1 -2
- data/lib/rubocop/cop/rails/strip_heredoc.rb +1 -1
- data/lib/rubocop/cop/rails/strong_parameters_expect.rb +104 -0
- data/lib/rubocop/cop/rails/three_state_boolean_column.rb +4 -5
- data/lib/rubocop/cop/rails/time_zone.rb +26 -11
- data/lib/rubocop/cop/rails/transaction_exit_statement.rb +40 -9
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +11 -26
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +17 -21
- data/lib/rubocop/cop/rails/unknown_env.rb +5 -1
- data/lib/rubocop/cop/rails/unused_ignored_columns.rb +6 -0
- data/lib/rubocop/cop/rails/unused_render_content.rb +67 -0
- data/lib/rubocop/cop/rails/validation.rb +9 -4
- data/lib/rubocop/cop/rails/where_equals.rb +29 -12
- data/lib/rubocop/cop/rails/where_exists.rb +9 -9
- data/lib/rubocop/cop/rails/where_missing.rb +6 -2
- data/lib/rubocop/cop/rails/where_not.rb +18 -11
- data/lib/rubocop/cop/rails/where_range.rb +203 -0
- data/lib/rubocop/cop/rails_cops.rb +11 -0
- data/lib/rubocop/rails/migration_file_skippable.rb +54 -0
- data/lib/rubocop/rails/plugin.rb +48 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +8 -7
- data/lib/rubocop/rails/schema_loader.rb +5 -15
- data/lib/rubocop/rails/version.rb +1 -1
- data/lib/rubocop/rails.rb +1 -8
- data/lib/rubocop-rails.rb +12 -4
- metadata +55 -11
- data/lib/rubocop/rails/inject.rb +0 -18
@@ -17,7 +17,7 @@ module RuboCop
|
|
17
17
|
# # good
|
18
18
|
# def change
|
19
19
|
# change_table :users do |t|
|
20
|
-
# t.remove :name, :string
|
20
|
+
# t.remove :name, type: :string
|
21
21
|
# end
|
22
22
|
# end
|
23
23
|
#
|
@@ -215,8 +215,10 @@ module RuboCop
|
|
215
215
|
end
|
216
216
|
|
217
217
|
def check_drop_table_node(node)
|
218
|
+
return unless (last_argument = node.last_argument)
|
219
|
+
|
218
220
|
drop_table_call(node) do
|
219
|
-
unless node.parent.
|
221
|
+
unless node.parent.any_block_type? || last_argument.block_pass_type?
|
220
222
|
add_offense(node, message: format(MSG, action: 'drop_table(without block)'))
|
221
223
|
end
|
222
224
|
end
|
@@ -290,10 +292,10 @@ module RuboCop
|
|
290
292
|
when :change
|
291
293
|
false
|
292
294
|
when :remove
|
293
|
-
target_rails_version >= 6.1 && all_hash_key?(node.
|
295
|
+
target_rails_version >= 6.1 && all_hash_key?(node.last_argument, :type)
|
294
296
|
when :change_default, :change_column_default, :change_table_comment,
|
295
297
|
:change_column_comment
|
296
|
-
all_hash_key?(node.
|
298
|
+
all_hash_key?(node.last_argument, :from, :to)
|
297
299
|
else
|
298
300
|
true
|
299
301
|
end
|
@@ -307,7 +309,7 @@ module RuboCop
|
|
307
309
|
|
308
310
|
def within_reversible_or_up_only_block?(node)
|
309
311
|
node.each_ancestor(:block).any? do |ancestor|
|
310
|
-
(ancestor.block_type? && ancestor.
|
312
|
+
(ancestor.block_type? && ancestor.method?(:reversible)) || ancestor.method?(:up_only)
|
311
313
|
end
|
312
314
|
end
|
313
315
|
|
@@ -12,7 +12,7 @@ module RuboCop
|
|
12
12
|
# `Style/FileRead`, `Style/FileWrite` and `Rails/RootJoinChain`.
|
13
13
|
#
|
14
14
|
# @safety
|
15
|
-
# This cop is unsafe for autocorrection because
|
15
|
+
# This cop is unsafe for autocorrection because ``Dir``'s `children`, `each_child`, `entries`, and `glob`
|
16
16
|
# methods return string element, but these methods of `Pathname` return `Pathname` element.
|
17
17
|
#
|
18
18
|
# @example
|
@@ -23,6 +23,8 @@ module RuboCop
|
|
23
23
|
# File.binread(Rails.root.join('db', 'schema.rb'))
|
24
24
|
# File.write(Rails.root.join('db', 'schema.rb'), content)
|
25
25
|
# File.binwrite(Rails.root.join('db', 'schema.rb'), content)
|
26
|
+
# Dir.glob(Rails.root.join('db', 'schema.rb'))
|
27
|
+
# Dir[Rails.root.join('db', 'schema.rb')]
|
26
28
|
#
|
27
29
|
# # good
|
28
30
|
# Rails.root.join('db', 'schema.rb').open
|
@@ -31,14 +33,30 @@ module RuboCop
|
|
31
33
|
# Rails.root.join('db', 'schema.rb').binread
|
32
34
|
# Rails.root.join('db', 'schema.rb').write(content)
|
33
35
|
# Rails.root.join('db', 'schema.rb').binwrite(content)
|
36
|
+
# Rails.root.glob("db/schema.rb")
|
34
37
|
#
|
35
|
-
class RootPathnameMethods < Base
|
38
|
+
class RootPathnameMethods < Base # rubocop:disable Metrics/ClassLength
|
36
39
|
extend AutoCorrector
|
37
40
|
include RangeHelp
|
38
41
|
|
39
|
-
MSG = '`%<rails_root>s` is a `Pathname
|
42
|
+
MSG = '`%<rails_root>s` is a `Pathname`, so you can use `%<replacement>s`.'
|
40
43
|
|
41
|
-
|
44
|
+
DIR_GLOB_METHODS = %i[[] glob].to_set.freeze
|
45
|
+
|
46
|
+
DIR_NON_GLOB_METHODS = %i[
|
47
|
+
children
|
48
|
+
delete
|
49
|
+
each_child
|
50
|
+
empty?
|
51
|
+
entries
|
52
|
+
exist?
|
53
|
+
mkdir
|
54
|
+
open
|
55
|
+
rmdir
|
56
|
+
unlink
|
57
|
+
].to_set.freeze
|
58
|
+
|
59
|
+
DIR_METHODS = (DIR_GLOB_METHODS + DIR_NON_GLOB_METHODS).freeze
|
42
60
|
|
43
61
|
FILE_METHODS = %i[
|
44
62
|
atime
|
@@ -134,7 +152,8 @@ module RuboCop
|
|
134
152
|
|
135
153
|
RESTRICT_ON_SEND = (DIR_METHODS + FILE_METHODS + FILE_TEST_METHODS + FILE_UTILS_METHODS).to_set.freeze
|
136
154
|
|
137
|
-
|
155
|
+
# @!method pathname_method_for_ruby_2_5_or_higher(node)
|
156
|
+
def_node_matcher :pathname_method_for_ruby_2_5_or_higher, <<~PATTERN
|
138
157
|
{
|
139
158
|
(send (const {nil? cbase} :Dir) $DIR_METHODS $_ $...)
|
140
159
|
(send (const {nil? cbase} {:IO :File}) $FILE_METHODS $_ $...)
|
@@ -143,9 +162,19 @@ module RuboCop
|
|
143
162
|
}
|
144
163
|
PATTERN
|
145
164
|
|
165
|
+
# @!method pathname_method_for_ruby_2_4_or_lower(node)
|
166
|
+
def_node_matcher :pathname_method_for_ruby_2_4_or_lower, <<~PATTERN
|
167
|
+
{
|
168
|
+
(send (const {nil? cbase} :Dir) $DIR_NON_GLOB_METHODS $_ $...)
|
169
|
+
(send (const {nil? cbase} {:IO :File}) $FILE_METHODS $_ $...)
|
170
|
+
(send (const {nil? cbase} :FileTest) $FILE_TEST_METHODS $_ $...)
|
171
|
+
(send (const {nil? cbase} :FileUtils) $FILE_UTILS_METHODS $_ $...)
|
172
|
+
}
|
173
|
+
PATTERN
|
174
|
+
|
146
175
|
def_node_matcher :dir_glob?, <<~PATTERN
|
147
176
|
(send
|
148
|
-
(const {cbase nil?} :Dir)
|
177
|
+
(const {cbase nil?} :Dir) DIR_GLOB_METHODS ...)
|
149
178
|
PATTERN
|
150
179
|
|
151
180
|
def_node_matcher :rails_root_pathname?, <<~PATTERN
|
@@ -162,13 +191,14 @@ module RuboCop
|
|
162
191
|
|
163
192
|
def on_send(node)
|
164
193
|
evidence(node) do |method, path, args, rails_root|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
end
|
194
|
+
replacement = if dir_glob?(node)
|
195
|
+
build_path_glob_replacement(path)
|
196
|
+
else
|
197
|
+
build_path_replacement(path, method, args)
|
198
|
+
end
|
171
199
|
|
200
|
+
message = format(MSG, rails_root: rails_root.source, replacement: replacement)
|
201
|
+
add_offense(node, message: message) do |corrector|
|
172
202
|
corrector.replace(node, replacement)
|
173
203
|
end
|
174
204
|
end
|
@@ -183,12 +213,20 @@ module RuboCop
|
|
183
213
|
yield(method, path, args, rails_root)
|
184
214
|
end
|
185
215
|
|
186
|
-
def
|
216
|
+
def pathname_method(node)
|
217
|
+
if target_ruby_version >= 2.5
|
218
|
+
pathname_method_for_ruby_2_5_or_higher(node)
|
219
|
+
else
|
220
|
+
pathname_method_for_ruby_2_4_or_lower(node)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def build_path_glob_replacement(path)
|
187
225
|
receiver = range_between(path.source_range.begin_pos, path.children.first.loc.selector.end_pos).source
|
188
226
|
|
189
227
|
argument = path.arguments.one? ? path.first_argument.source : join_arguments(path.arguments)
|
190
228
|
|
191
|
-
"#{receiver}
|
229
|
+
"#{receiver}.glob(#{argument})"
|
192
230
|
end
|
193
231
|
|
194
232
|
def build_path_replacement(path, method, args)
|
@@ -199,7 +237,12 @@ module RuboCop
|
|
199
237
|
end
|
200
238
|
|
201
239
|
replacement = "#{path_replacement}.#{method}"
|
202
|
-
|
240
|
+
|
241
|
+
if args.any?
|
242
|
+
formatted_args = args.map { |arg| arg.array_type? ? "*#{arg.source}" : arg.source }
|
243
|
+
replacement += "(#{formatted_args.join(', ')})"
|
244
|
+
end
|
245
|
+
|
203
246
|
replacement
|
204
247
|
end
|
205
248
|
|
@@ -182,13 +182,13 @@ module RuboCop
|
|
182
182
|
def right_assignment_node(assignment)
|
183
183
|
node = assignment.node.child_nodes.first
|
184
184
|
|
185
|
-
return node unless node&.
|
185
|
+
return node unless node&.any_block_type?
|
186
186
|
|
187
187
|
node.send_node
|
188
188
|
end
|
189
189
|
|
190
190
|
def persisted_referenced?(assignment)
|
191
|
-
return unless assignment.referenced?
|
191
|
+
return false unless assignment.referenced?
|
192
192
|
|
193
193
|
assignment.variable.references.any? do |reference|
|
194
194
|
call_to_persisted?(reference.node.parent)
|
@@ -196,6 +196,8 @@ module RuboCop
|
|
196
196
|
end
|
197
197
|
|
198
198
|
def call_to_persisted?(node)
|
199
|
+
node = node.parent.condition if node.parenthesized_call? && node.parent.if_type?
|
200
|
+
|
199
201
|
node.send_type? && node.method?(:persisted?)
|
200
202
|
end
|
201
203
|
|
@@ -235,18 +237,23 @@ module RuboCop
|
|
235
237
|
|
236
238
|
def in_condition_or_compound_boolean?(node)
|
237
239
|
node = node.block_node || node
|
238
|
-
parent = node.
|
240
|
+
parent = node.each_ancestor.find { |ancestor| !ancestor.begin_type? }
|
239
241
|
return false unless parent
|
240
242
|
|
241
|
-
operator_or_single_negative?(parent) || (conditional?(parent) && node == parent.condition)
|
243
|
+
operator_or_single_negative?(parent) || (conditional?(parent) && node == deparenthesize(parent.condition))
|
242
244
|
end
|
243
245
|
|
244
246
|
def operator_or_single_negative?(node)
|
245
|
-
node.
|
247
|
+
node.operator_keyword? || single_negative?(node)
|
246
248
|
end
|
247
249
|
|
248
250
|
def conditional?(parent)
|
249
|
-
parent.
|
251
|
+
parent.type?(:if, :case)
|
252
|
+
end
|
253
|
+
|
254
|
+
def deparenthesize(node)
|
255
|
+
node = node.children.last while node.begin_type?
|
256
|
+
node
|
250
257
|
end
|
251
258
|
|
252
259
|
def checked_immediately?(node)
|
@@ -298,7 +305,7 @@ module RuboCop
|
|
298
305
|
|
299
306
|
node = assignable_node(node)
|
300
307
|
method, sibling_index = find_method_with_sibling_index(node.parent)
|
301
|
-
return unless method
|
308
|
+
return false unless method&.type?(:def, :any_block)
|
302
309
|
|
303
310
|
method.children.size == node.sibling_index + sibling_index
|
304
311
|
end
|
@@ -317,12 +324,13 @@ module RuboCop
|
|
317
324
|
|
318
325
|
def explicit_return?(node)
|
319
326
|
ret = assignable_node(node).parent
|
320
|
-
ret
|
327
|
+
ret&.type?(:return, :next)
|
321
328
|
end
|
322
329
|
|
323
330
|
def return_value_assigned?(node)
|
324
|
-
assignment = assignable_node(node).parent
|
325
|
-
|
331
|
+
return false unless (assignment = assignable_node(node).parent)
|
332
|
+
|
333
|
+
assignment.assignment?
|
326
334
|
end
|
327
335
|
|
328
336
|
def persist_method?(node, methods = RESTRICT_ON_SEND)
|
@@ -331,10 +339,10 @@ module RuboCop
|
|
331
339
|
|
332
340
|
# Check argument signature as no arguments or one hash
|
333
341
|
def expected_signature?(node)
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
342
|
+
return true unless node.arguments?
|
343
|
+
return false if !node.arguments.one? || node.method?(:destroy)
|
344
|
+
|
345
|
+
node.first_argument.hash_type? || !node.first_argument.literal?
|
338
346
|
end
|
339
347
|
end
|
340
348
|
end
|
@@ -23,6 +23,7 @@ module RuboCop
|
|
23
23
|
#
|
24
24
|
class SchemaComment < Base
|
25
25
|
include ActiveRecordMigrationsHelper
|
26
|
+
include MigrationsHelper
|
26
27
|
|
27
28
|
COLUMN_MSG = 'New database column without `comment`.'
|
28
29
|
TABLE_MSG = 'New database table without `comment`.'
|
@@ -74,17 +75,25 @@ module RuboCop
|
|
74
75
|
def on_send(node)
|
75
76
|
if add_column_without_comment?(node)
|
76
77
|
add_offense(node, message: COLUMN_MSG)
|
77
|
-
elsif
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
add_offense(node.parent.body, message: COLUMN_MSG)
|
82
|
-
end
|
78
|
+
elsif create_table_without_comment?(node)
|
79
|
+
add_offense(node, message: TABLE_MSG)
|
80
|
+
elsif create_table_with_block?(node.parent)
|
81
|
+
check_column_within_create_table_block(node.parent.body)
|
83
82
|
end
|
84
83
|
end
|
85
84
|
|
86
85
|
private
|
87
86
|
|
87
|
+
def check_column_within_create_table_block(node)
|
88
|
+
if node.begin_type?
|
89
|
+
node.child_nodes.each do |child_node|
|
90
|
+
add_offense(child_node, message: COLUMN_MSG) if t_column_without_comment?(child_node)
|
91
|
+
end
|
92
|
+
elsif t_column_without_comment?(node)
|
93
|
+
add_offense(node, message: COLUMN_MSG)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
88
97
|
def add_column_without_comment?(node)
|
89
98
|
add_column?(node) && !add_column_with_comment?(node)
|
90
99
|
end
|
@@ -93,10 +102,8 @@ module RuboCop
|
|
93
102
|
create_table?(node) && !create_table_with_comment?(node)
|
94
103
|
end
|
95
104
|
|
96
|
-
def
|
97
|
-
|
98
|
-
t_column?(node.parent.body) &&
|
99
|
-
!t_column_with_comment?(node.parent.body)
|
105
|
+
def t_column_without_comment?(node)
|
106
|
+
t_column?(node) && !t_column_with_comment?(node)
|
100
107
|
end
|
101
108
|
end
|
102
109
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Checks for uses of `select(:column_name)` with `map(&:column_name)`.
|
7
|
+
# These can be replaced with `pluck(:column_name)`.
|
8
|
+
#
|
9
|
+
# There also should be some performance improvement since it skips instantiating the model class for matches.
|
10
|
+
#
|
11
|
+
# @safety
|
12
|
+
# This cop is unsafe because the model might override the attribute getter.
|
13
|
+
# Additionally, the model's `after_initialize` hooks are skipped when using `pluck`.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# # bad
|
17
|
+
# Model.select(:column_name).map(&:column_name)
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# Model.pluck(:column_name)
|
21
|
+
#
|
22
|
+
class SelectMap < Base
|
23
|
+
extend AutoCorrector
|
24
|
+
|
25
|
+
MSG = 'Use `%<preferred_method>s` instead of `select` with `%<map_method>s`.'
|
26
|
+
|
27
|
+
RESTRICT_ON_SEND = %i[map collect].freeze
|
28
|
+
|
29
|
+
def on_send(node)
|
30
|
+
return unless node.first_argument
|
31
|
+
|
32
|
+
column_name = node.first_argument.source.delete_prefix('&:')
|
33
|
+
return unless (select_node = find_select_node(node, column_name))
|
34
|
+
|
35
|
+
offense_range = select_node.loc.selector.begin.join(node.source_range.end)
|
36
|
+
preferred_method = "pluck(:#{column_name})"
|
37
|
+
message = format(MSG, preferred_method: preferred_method, map_method: node.method_name)
|
38
|
+
|
39
|
+
add_offense(offense_range, message: message) do |corrector|
|
40
|
+
autocorrect(corrector, select_node, node, preferred_method)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
alias on_csend on_send
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def find_select_node(node, column_name)
|
48
|
+
node.descendants.detect do |select_candidate|
|
49
|
+
next if !select_candidate.call_type? || !select_candidate.method?(:select)
|
50
|
+
|
51
|
+
match_column_name?(select_candidate, column_name)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# rubocop:disable Metrics/AbcSize
|
56
|
+
def autocorrect(corrector, select_node, node, preferred_method)
|
57
|
+
corrector.remove(select_node.parent.loc.dot)
|
58
|
+
corrector.remove(select_node.loc.selector.begin.join(select_node.source_range.end))
|
59
|
+
corrector.replace(node.loc.selector.begin.join(node.source_range.end), preferred_method)
|
60
|
+
end
|
61
|
+
# rubocop:enable Metrics/AbcSize
|
62
|
+
|
63
|
+
def match_column_name?(select_candidate, column_name)
|
64
|
+
return false unless select_candidate.arguments.one?
|
65
|
+
return false unless (first_argument = select_candidate.first_argument)
|
66
|
+
|
67
|
+
argument = case select_candidate.first_argument.type
|
68
|
+
when :sym
|
69
|
+
first_argument.source.delete_prefix(':')
|
70
|
+
when :str
|
71
|
+
first_argument.value if first_argument.respond_to?(:value)
|
72
|
+
end
|
73
|
+
|
74
|
+
argument == column_name
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -9,6 +9,9 @@ module RuboCop
|
|
9
9
|
#
|
10
10
|
# Methods may be ignored from this rule by configuring a `AllowedMethods`.
|
11
11
|
#
|
12
|
+
# @safety
|
13
|
+
# This cop is unsafe if the receiver object is not an Active Record object.
|
14
|
+
#
|
12
15
|
# @example
|
13
16
|
# # bad
|
14
17
|
# Article.first.decrement!(:view_count)
|
@@ -58,12 +61,12 @@ module RuboCop
|
|
58
61
|
def_node_matcher :good_touch?, <<~PATTERN
|
59
62
|
{
|
60
63
|
(send (const {nil? cbase} :FileUtils) :touch ...)
|
61
|
-
(send _ :touch
|
64
|
+
(send _ :touch boolean)
|
62
65
|
}
|
63
66
|
PATTERN
|
64
67
|
|
65
68
|
def_node_matcher :good_insert?, <<~PATTERN
|
66
|
-
(
|
69
|
+
(call _ {:insert :insert!} _ {
|
67
70
|
!(hash ...)
|
68
71
|
(hash <(pair (sym !{:returning :unique_by}) _) ...>)
|
69
72
|
} ...)
|
@@ -97,7 +100,8 @@ module RuboCop
|
|
97
100
|
end
|
98
101
|
|
99
102
|
def forbidden_methods
|
100
|
-
|
103
|
+
# TODO: Remove when RuboCop Rails 3 releases.
|
104
|
+
obsolete_result = cop_config['Blacklist'] # rubocop:disable InternalAffairs/UndefinedConfig
|
101
105
|
if obsolete_result
|
102
106
|
warn '`Blacklist` has been renamed to `ForbiddenMethods`.' unless @displayed_forbidden_warning
|
103
107
|
@displayed_forbidden_warning = true
|
@@ -108,7 +112,8 @@ module RuboCop
|
|
108
112
|
end
|
109
113
|
|
110
114
|
def allowed_methods
|
111
|
-
|
115
|
+
# TODO: Remove when RuboCop Rails 3 releases.
|
116
|
+
obsolete_result = cop_config['Whitelist'] # rubocop:disable InternalAffairs/UndefinedConfig
|
112
117
|
if obsolete_result
|
113
118
|
warn '`Whitelist` has been renamed to `AllowedMethods`.' unless @displayed_allowed_warning
|
114
119
|
@displayed_allowed_warning = true
|
@@ -3,7 +3,6 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
7
6
|
# Checks SQL heredocs to use `.squish`.
|
8
7
|
#
|
9
8
|
# @safety
|
@@ -69,7 +68,7 @@ module RuboCop
|
|
69
68
|
end
|
70
69
|
|
71
70
|
def using_squish?(node)
|
72
|
-
node.parent&.send_type? && node.parent
|
71
|
+
node.parent&.send_type? && node.parent.method?(:squish)
|
73
72
|
end
|
74
73
|
|
75
74
|
def singleline_comments_present?(node)
|
@@ -33,7 +33,7 @@ module RuboCop
|
|
33
33
|
|
34
34
|
def on_send(node)
|
35
35
|
return unless (receiver = node.receiver)
|
36
|
-
return unless receiver.
|
36
|
+
return unless receiver.type?(:str, :dstr)
|
37
37
|
return unless receiver.respond_to?(:heredoc?) && receiver.heredoc?
|
38
38
|
|
39
39
|
register_offense(node, receiver)
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Enforces the use of `ActionController::Parameters#expect` as a method for strong parameter handling.
|
7
|
+
#
|
8
|
+
# @safety
|
9
|
+
# This cop's autocorrection is considered unsafe because there are cases where the HTTP status may change
|
10
|
+
# from 500 to 400 when handling invalid parameters. This change, however, reflects an intentional
|
11
|
+
# incompatibility introduced for valid reasons by the `expect` method, which aligns better with
|
12
|
+
# strong parameter conventions.
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
#
|
16
|
+
# # bad
|
17
|
+
# params.require(:user).permit(:name, :age)
|
18
|
+
# params.permit(user: [:name, :age]).require(:user)
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# params.expect(user: [:name, :age])
|
22
|
+
#
|
23
|
+
class StrongParametersExpect < Base
|
24
|
+
extend AutoCorrector
|
25
|
+
extend TargetRailsVersion
|
26
|
+
|
27
|
+
MSG = 'Use `%<prefer>s` instead.'
|
28
|
+
RESTRICT_ON_SEND = %i[require permit].freeze
|
29
|
+
|
30
|
+
minimum_target_rails_version 8.0
|
31
|
+
|
32
|
+
def_node_matcher :params_require_permit, <<~PATTERN
|
33
|
+
$(call
|
34
|
+
$(call
|
35
|
+
(send nil? :params) :require _) :permit _+)
|
36
|
+
PATTERN
|
37
|
+
|
38
|
+
def_node_matcher :params_permit_require, <<~PATTERN
|
39
|
+
$(call
|
40
|
+
$(call
|
41
|
+
(send nil? :params) :permit (hash (pair _require_param_name _ )))
|
42
|
+
:require _require_param_name)
|
43
|
+
PATTERN
|
44
|
+
|
45
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
46
|
+
def on_send(node)
|
47
|
+
return if part_of_ignored_node?(node)
|
48
|
+
|
49
|
+
if (permit_method, require_method = params_require_permit(node))
|
50
|
+
range = offense_range(require_method, node)
|
51
|
+
prefer = expect_method(require_method, permit_method)
|
52
|
+
replace_argument = true
|
53
|
+
elsif (require_method, permit_method = params_permit_require(node))
|
54
|
+
range = offense_range(permit_method, node)
|
55
|
+
prefer = "expect(#{permit_method.arguments.map(&:source).join(', ')})"
|
56
|
+
replace_argument = false
|
57
|
+
else
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
add_offense(range, message: format(MSG, prefer: prefer)) do |corrector|
|
62
|
+
corrector.remove(require_method.receiver.source_range.end.join(require_method.source_range.end))
|
63
|
+
corrector.replace(permit_method.loc.selector, 'expect')
|
64
|
+
if replace_argument
|
65
|
+
corrector.insert_before(permit_method.first_argument, "#{require_key(require_method)}[")
|
66
|
+
corrector.insert_after(permit_method.last_argument, ']')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
ignore_node(node)
|
71
|
+
end
|
72
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
73
|
+
alias on_csend on_send
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def offense_range(method_node, node)
|
78
|
+
method_node.loc.selector.join(node.source_range.end)
|
79
|
+
end
|
80
|
+
|
81
|
+
def expect_method(require_method, permit_method)
|
82
|
+
require_key = require_key(require_method)
|
83
|
+
permit_args = permit_method.arguments.map(&:source).join(', ')
|
84
|
+
|
85
|
+
arguments = "#{require_key}[#{permit_args}]"
|
86
|
+
|
87
|
+
"expect(#{arguments})"
|
88
|
+
end
|
89
|
+
|
90
|
+
def require_key(require_method)
|
91
|
+
if (first_argument = require_method.first_argument).respond_to?(:value)
|
92
|
+
require_arg = first_argument.value
|
93
|
+
separator = ': '
|
94
|
+
else
|
95
|
+
require_arg = first_argument.source
|
96
|
+
separator = ' => '
|
97
|
+
end
|
98
|
+
|
99
|
+
"#{require_arg}#{separator}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -18,8 +18,9 @@ module RuboCop
|
|
18
18
|
# t.boolean :active, default: true, null: false
|
19
19
|
#
|
20
20
|
class ThreeStateBooleanColumn < Base
|
21
|
-
|
21
|
+
include MigrationsHelper
|
22
22
|
|
23
|
+
MSG = 'Boolean columns should always have a default value and a `NOT NULL` constraint.'
|
23
24
|
RESTRICT_ON_SEND = %i[add_column column boolean].freeze
|
24
25
|
|
25
26
|
def_node_matcher :three_state_boolean?, <<~PATTERN
|
@@ -35,7 +36,7 @@ module RuboCop
|
|
35
36
|
PATTERN
|
36
37
|
|
37
38
|
def_node_search :change_column_null?, <<~PATTERN
|
38
|
-
(send nil? :change_column_null
|
39
|
+
(send nil? :change_column_null %1 %2 false)
|
39
40
|
PATTERN
|
40
41
|
|
41
42
|
def on_send(node)
|
@@ -46,9 +47,7 @@ module RuboCop
|
|
46
47
|
|
47
48
|
def_node = node.each_ancestor(:def, :defs).first
|
48
49
|
table_node = table_node(node)
|
49
|
-
if def_node && (table_node.nil? || change_column_null?(def_node, table_node
|
50
|
-
return
|
51
|
-
end
|
50
|
+
return if def_node && (table_node.nil? || change_column_null?(def_node, table_node, column_node))
|
52
51
|
|
53
52
|
add_offense(node)
|
54
53
|
end
|