rubocop-rails 2.15.2 → 2.16.1

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +81 -0
  3. data/config/obsoletion.yml +10 -0
  4. data/lib/rubocop/cop/mixin/active_record_helper.rb +1 -4
  5. data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +1 -3
  6. data/lib/rubocop/cop/mixin/index_method.rb +5 -15
  7. data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +98 -0
  8. data/lib/rubocop/cop/rails/action_filter.rb +1 -1
  9. data/lib/rubocop/cop/rails/active_record_aliases.rb +1 -4
  10. data/lib/rubocop/cop/rails/active_record_override.rb +2 -5
  11. data/lib/rubocop/cop/rails/active_support_on_load.rb +70 -0
  12. data/lib/rubocop/cop/rails/add_column_index.rb +1 -4
  13. data/lib/rubocop/cop/rails/blank.rb +1 -2
  14. data/lib/rubocop/cop/rails/bulk_change_table.rb +6 -20
  15. data/lib/rubocop/cop/rails/compact_blank.rb +5 -1
  16. data/lib/rubocop/cop/rails/content_tag.rb +1 -3
  17. data/lib/rubocop/cop/rails/date.rb +4 -9
  18. data/lib/rubocop/cop/rails/delegate.rb +2 -5
  19. data/lib/rubocop/cop/rails/deprecated_active_model_errors_methods.rb +17 -13
  20. data/lib/rubocop/cop/rails/dot_separated_keys.rb +1 -1
  21. data/lib/rubocop/cop/rails/dynamic_find_by.rb +2 -4
  22. data/lib/rubocop/cop/rails/enum_uniqueness.rb +2 -5
  23. data/lib/rubocop/cop/rails/environment_comparison.rb +1 -2
  24. data/lib/rubocop/cop/rails/file_path.rb +2 -4
  25. data/lib/rubocop/cop/rails/find_each.rb +8 -2
  26. data/lib/rubocop/cop/rails/freeze_time.rb +74 -0
  27. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +1 -3
  28. data/lib/rubocop/cop/rails/http_positional_arguments.rb +4 -9
  29. data/lib/rubocop/cop/rails/http_status.rb +5 -10
  30. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +3 -10
  31. data/lib/rubocop/cop/rails/inverse_of.rb +3 -6
  32. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +2 -6
  33. data/lib/rubocop/cop/rails/link_to_blank.rb +1 -4
  34. data/lib/rubocop/cop/rails/output.rb +2 -5
  35. data/lib/rubocop/cop/rails/pluralization_grammar.rb +1 -2
  36. data/lib/rubocop/cop/rails/presence.rb +1 -3
  37. data/lib/rubocop/cop/rails/present.rb +3 -6
  38. data/lib/rubocop/cop/rails/rake_environment.rb +1 -1
  39. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +2 -4
  40. data/lib/rubocop/cop/rails/redundant_foreign_key.rb +1 -1
  41. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +2 -2
  42. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +28 -26
  43. data/lib/rubocop/cop/rails/reflection_class_name.rb +17 -0
  44. data/lib/rubocop/cop/rails/refute_methods.rb +1 -5
  45. data/lib/rubocop/cop/rails/relative_date_constant.rb +2 -5
  46. data/lib/rubocop/cop/rails/request_referer.rb +1 -2
  47. data/lib/rubocop/cop/rails/reversible_migration.rb +10 -33
  48. data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +1 -2
  49. data/lib/rubocop/cop/rails/root_pathname_methods.rb +214 -0
  50. data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +1 -3
  51. data/lib/rubocop/cop/rails/save_bang.rb +10 -22
  52. data/lib/rubocop/cop/rails/short_i18n.rb +1 -4
  53. data/lib/rubocop/cop/rails/skips_model_validations.rb +1 -2
  54. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +1 -5
  55. data/lib/rubocop/cop/rails/time_zone.rb +8 -19
  56. data/lib/rubocop/cop/rails/to_s_with_argument.rb +41 -0
  57. data/lib/rubocop/cop/rails/top_level_hash_with_indifferent_access.rb +49 -0
  58. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +3 -6
  59. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +1 -3
  60. data/lib/rubocop/cop/rails/unknown_env.rb +2 -4
  61. data/lib/rubocop/cop/rails/validation.rb +4 -12
  62. data/lib/rubocop/cop/rails/where_missing.rb +111 -0
  63. data/lib/rubocop/cop/rails_cops.rb +7 -0
  64. data/lib/rubocop/rails/version.rb +1 -1
  65. metadata +13 -8
  66. data/bin/console +0 -11
  67. data/bin/setup +0 -7
@@ -81,11 +81,7 @@ module RuboCop
81
81
  end
82
82
 
83
83
  def offense_message(method_name)
84
- format(
85
- MSG,
86
- bad_method: method_name,
87
- good_method: convert_good_method(method_name)
88
- )
84
+ format(MSG, bad_method: method_name, good_method: convert_good_method(method_name))
89
85
  end
90
86
 
91
87
  def convert_good_method(bad_method)
@@ -34,8 +34,7 @@ module RuboCop
34
34
  include RangeHelp
35
35
  extend AutoCorrector
36
36
 
37
- MSG = 'Do not assign `%<method_name>s` to constants as it ' \
38
- 'will be evaluated only once.'
37
+ MSG = 'Do not assign `%<method_name>s` to constants as it will be evaluated only once.'
39
38
  RELATIVE_DATE_METHODS = %i[since from_now after ago until before yesterday tomorrow].to_set.freeze
40
39
 
41
40
  def on_casgn(node)
@@ -77,9 +76,7 @@ module RuboCop
77
76
  return unless scope.nil?
78
77
 
79
78
  indent = ' ' * node.loc.column
80
- new_code = ["def self.#{const_name.downcase}",
81
- "#{indent}#{value.source}",
82
- 'end'].join("\n#{indent}")
79
+ new_code = ["def self.#{const_name.downcase}", "#{indent}#{value.source}", 'end'].join("\n#{indent}")
83
80
 
84
81
  corrector.replace(node.source_range, new_code)
85
82
  end
@@ -23,8 +23,7 @@ module RuboCop
23
23
  include ConfigurableEnforcedStyle
24
24
  extend AutoCorrector
25
25
 
26
- MSG = 'Use `request.%<prefer>s` instead of ' \
27
- '`request.%<current>s`.'
26
+ MSG = 'Use `request.%<prefer>s` instead of `request.%<current>s`.'
28
27
  RESTRICT_ON_SEND = %i[referer referrer].freeze
29
28
 
30
29
  def_node_matcher :referer?, <<~PATTERN
@@ -229,6 +229,8 @@ module RuboCop
229
229
  check_change_table_node(node.send_node, node.body)
230
230
  end
231
231
 
232
+ alias on_numblock on_block
233
+
232
234
  private
233
235
 
234
236
  def check_irreversible_schema_statement_node(node)
@@ -240,10 +242,7 @@ module RuboCop
240
242
  def check_drop_table_node(node)
241
243
  drop_table_call(node) do
242
244
  unless node.parent.block_type? || node.last_argument.block_pass_type?
243
- add_offense(
244
- node,
245
- message: format(MSG, action: 'drop_table(without block)')
246
- )
245
+ add_offense(node, message: format(MSG, action: 'drop_table(without block)'))
247
246
  end
248
247
  end
249
248
  end
@@ -251,22 +250,12 @@ module RuboCop
251
250
  def check_reversible_hash_node(node)
252
251
  return if reversible_change_table_call?(node)
253
252
 
254
- add_offense(
255
- node,
256
- message: format(
257
- MSG, action: "#{node.method_name}(without :from and :to)"
258
- )
259
- )
253
+ add_offense(node, message: format(MSG, action: "#{node.method_name}(without :from and :to)"))
260
254
  end
261
255
 
262
256
  def check_remove_column_node(node)
263
257
  remove_column_call(node) do |args|
264
- if args.to_a.size < 3
265
- add_offense(
266
- node,
267
- message: format(MSG, action: 'remove_column(without type)')
268
- )
269
- end
258
+ add_offense(node, message: format(MSG, action: 'remove_column(without type)')) if args.to_a.size < 3
270
259
  end
271
260
  end
272
261
 
@@ -295,10 +284,7 @@ module RuboCop
295
284
  unless all_hash_key?(args, :type) && target_rails_version >= 6.1
296
285
  action = target_rails_version >= 6.1 ? 'remove_columns(without type)' : 'remove_columns'
297
286
 
298
- add_offense(
299
- node,
300
- message: format(MSG, action: action)
301
- )
287
+ add_offense(node, message: format(MSG, action: action))
302
288
  end
303
289
  end
304
290
  end
@@ -306,18 +292,14 @@ module RuboCop
306
292
  def check_remove_index_node(node)
307
293
  remove_index_call(node) do |args|
308
294
  if args.hash_type? && !all_hash_key?(args, :column)
309
- add_offense(
310
- node,
311
- message: format(MSG, action: 'remove_index(without column)')
312
- )
295
+ add_offense(node, message: format(MSG, action: 'remove_index(without column)'))
313
296
  end
314
297
  end
315
298
  end
316
299
 
317
300
  def check_change_table_offense(receiver, node)
318
301
  method_name = node.method_name
319
- return if receiver != node.receiver &&
320
- reversible_change_table_call?(node)
302
+ return if receiver != node.receiver && reversible_change_table_call?(node)
321
303
 
322
304
  action = if method_name == :remove
323
305
  target_rails_version >= 6.1 ? 't.remove (without type)' : 't.remove'
@@ -325,10 +307,7 @@ module RuboCop
325
307
  "change_table(with #{method_name})"
326
308
  end
327
309
 
328
- add_offense(
329
- node,
330
- message: format(MSG, action: action)
331
- )
310
+ add_offense(node, message: format(MSG, action: action))
332
311
  end
333
312
 
334
313
  def reversible_change_table_call?(node)
@@ -353,9 +332,7 @@ module RuboCop
353
332
 
354
333
  def within_reversible_or_up_only_block?(node)
355
334
  node.each_ancestor(:block).any? do |ancestor|
356
- (ancestor.block_type? &&
357
- ancestor.send_node.method?(:reversible)) ||
358
- ancestor.send_node.method?(:up_only)
335
+ (ancestor.block_type? && ancestor.send_node.method?(:reversible)) || ancestor.send_node.method?(:up_only)
359
336
  end
360
337
  end
361
338
 
@@ -45,8 +45,7 @@ module RuboCop
45
45
  class ReversibleMigrationMethodDefinition < Base
46
46
  include MigrationsHelper
47
47
 
48
- MSG = 'Migrations must contain either a `change` method, or ' \
49
- 'both an `up` and a `down` method.'
48
+ MSG = 'Migrations must contain either a `change` method, or both an `up` and a `down` method.'
50
49
 
51
50
  def_node_matcher :change_method?, <<~PATTERN
52
51
  `(def :change (args) _)
@@ -0,0 +1,214 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Use `Rails.root` IO methods instead of passing it to `File`.
7
+ #
8
+ # `Rails.root` is an instance of `Pathname`
9
+ # so we can apply many IO methods directly.
10
+ #
11
+ # This cop works best when used together with
12
+ # `Style/FileRead`, `Style/FileWrite` and `Rails/RootJoinChain`.
13
+ #
14
+ # @safety
15
+ # This cop is unsafe for autocorrection because `Dir`'s `children`, `each_child`, `entries`, and `glob`
16
+ # methods return string element, but these methods of `Pathname` return `Pathname` element.
17
+ #
18
+ # @example
19
+ # # bad
20
+ # File.open(Rails.root.join('db', 'schema.rb'))
21
+ # File.open(Rails.root.join('db', 'schema.rb'), 'w')
22
+ # File.read(Rails.root.join('db', 'schema.rb'))
23
+ # File.binread(Rails.root.join('db', 'schema.rb'))
24
+ # File.write(Rails.root.join('db', 'schema.rb'), content)
25
+ # File.binwrite(Rails.root.join('db', 'schema.rb'), content)
26
+ #
27
+ # # good
28
+ # Rails.root.join('db', 'schema.rb').open
29
+ # Rails.root.join('db', 'schema.rb').open('w')
30
+ # Rails.root.join('db', 'schema.rb').read
31
+ # Rails.root.join('db', 'schema.rb').binread
32
+ # Rails.root.join('db', 'schema.rb').write(content)
33
+ # Rails.root.join('db', 'schema.rb').binwrite(content)
34
+ #
35
+ class RootPathnameMethods < Base
36
+ extend AutoCorrector
37
+ include RangeHelp
38
+
39
+ MSG = '`%<rails_root>s` is a `Pathname` so you can just append `#%<method>s`.'
40
+
41
+ DIR_METHODS = %i[children delete each_child empty? entries exist? glob mkdir open rmdir unlink].to_set.freeze
42
+
43
+ FILE_METHODS = %i[
44
+ atime
45
+ basename
46
+ binread
47
+ binwrite
48
+ birthtime
49
+ blockdev?
50
+ chardev?
51
+ chmod
52
+ chown
53
+ ctime
54
+ delete
55
+ directory?
56
+ dirname
57
+ empty?
58
+ executable?
59
+ executable_real?
60
+ exist?
61
+ expand_path
62
+ extname
63
+ file?
64
+ fnmatch
65
+ fnmatch?
66
+ ftype
67
+ grpowned?
68
+ join
69
+ lchmod
70
+ lchown
71
+ lstat
72
+ mtime
73
+ open
74
+ owned?
75
+ pipe?
76
+ read
77
+ readable?
78
+ readable_real?
79
+ readlines
80
+ readlink
81
+ realdirpath
82
+ realpath
83
+ rename
84
+ setgid?
85
+ setuid?
86
+ size
87
+ size?
88
+ socket?
89
+ split
90
+ stat
91
+ sticky?
92
+ symlink?
93
+ sysopen
94
+ truncate
95
+ unlink
96
+ utime
97
+ world_readable?
98
+ world_writable?
99
+ writable?
100
+ writable_real?
101
+ write
102
+ zero?
103
+ ].to_set.freeze
104
+
105
+ FILE_TEST_METHODS = %i[
106
+ blockdev?
107
+ chardev?
108
+ directory?
109
+ empty?
110
+ executable?
111
+ executable_real?
112
+ exist?
113
+ file?
114
+ grpowned?
115
+ owned?
116
+ pipe?
117
+ readable?
118
+ readable_real?
119
+ setgid?
120
+ setuid?
121
+ size
122
+ size?
123
+ socket?
124
+ sticky?
125
+ symlink?
126
+ world_readable?
127
+ world_writable?
128
+ writable?
129
+ writable_real?
130
+ zero?
131
+ ].to_set.freeze
132
+
133
+ FILE_UTILS_METHODS = %i[chmod chown mkdir mkpath rmdir rmtree].to_set.freeze
134
+
135
+ RESTRICT_ON_SEND = (DIR_METHODS + FILE_METHODS + FILE_TEST_METHODS + FILE_UTILS_METHODS).to_set.freeze
136
+
137
+ def_node_matcher :pathname_method, <<~PATTERN
138
+ {
139
+ (send (const {nil? cbase} :Dir) $DIR_METHODS $_ $...)
140
+ (send (const {nil? cbase} {:IO :File}) $FILE_METHODS $_ $...)
141
+ (send (const {nil? cbase} :FileTest) $FILE_TEST_METHODS $_ $...)
142
+ (send (const {nil? cbase} :FileUtils) $FILE_UTILS_METHODS $_ $...)
143
+ }
144
+ PATTERN
145
+
146
+ def_node_matcher :dir_glob?, <<~PATTERN
147
+ (send
148
+ (const {cbase nil?} :Dir) :glob ...)
149
+ PATTERN
150
+
151
+ def_node_matcher :rails_root_pathname?, <<~PATTERN
152
+ {
153
+ $#rails_root?
154
+ (send $#rails_root? :join ...)
155
+ }
156
+ PATTERN
157
+
158
+ # @!method rails_root?(node)
159
+ def_node_matcher :rails_root?, <<~PATTERN
160
+ (send (const {nil? cbase} :Rails) {:root :public_path})
161
+ PATTERN
162
+
163
+ def on_send(node)
164
+ evidence(node) do |method, path, args, rails_root|
165
+ add_offense(node, message: format(MSG, method: method, rails_root: rails_root.source)) do |corrector|
166
+ if dir_glob?(node)
167
+ replacement = build_path_glob(path, method)
168
+ else
169
+ replacement = "#{path.source}.#{method}"
170
+ replacement += "(#{args.map(&:source).join(', ')})" unless args.empty?
171
+ end
172
+
173
+ corrector.replace(node, replacement)
174
+ end
175
+ end
176
+ end
177
+
178
+ private
179
+
180
+ def evidence(node)
181
+ return if node.method?(:open) && node.parent&.send_type?
182
+ return unless (method, path, args = pathname_method(node)) && (rails_root = rails_root_pathname?(path))
183
+
184
+ yield(method, path, args, rails_root)
185
+ end
186
+
187
+ def build_path_glob(path, method)
188
+ receiver = range_between(path.loc.expression.begin_pos, path.children.first.loc.selector.end_pos).source
189
+
190
+ argument = if path.arguments.one?
191
+ path.first_argument.source
192
+ else
193
+ join_arguments(path.arguments)
194
+ end
195
+
196
+ "#{receiver}.#{method}(#{argument})"
197
+ end
198
+
199
+ def include_interpolation?(arguments)
200
+ arguments.any? do |argument|
201
+ argument.children.any? { |child| child.respond_to?(:begin_type?) && child.begin_type? }
202
+ end
203
+ end
204
+
205
+ def join_arguments(arguments)
206
+ quote = include_interpolation?(arguments) ? '"' : "'"
207
+ joined_arguments = arguments.map(&:value).join('/')
208
+
209
+ "#{quote}#{joined_arguments}#{quote}"
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
@@ -31,9 +31,7 @@ module RuboCop
31
31
  class SafeNavigationWithBlank < Base
32
32
  extend AutoCorrector
33
33
 
34
- MSG =
35
- 'Avoid calling `blank?` with the safe navigation operator ' \
36
- 'in conditionals.'
34
+ MSG = 'Avoid calling `blank?` with the safe navigation operator in conditionals.'
37
35
 
38
36
  def_node_matcher :safe_navigation_blank_in_conditional?, <<~PATTERN
39
37
  (if $(csend ... :blank?) ...)
@@ -121,18 +121,12 @@ module RuboCop
121
121
  include NegativeConditional
122
122
  extend AutoCorrector
123
123
 
124
- MSG = 'Use `%<prefer>s` instead of `%<current>s` if the return ' \
125
- 'value is not checked.'
126
- CREATE_MSG = (MSG +
127
- ' Or check `persisted?` on model returned from ' \
128
- '`%<current>s`.').freeze
129
- CREATE_CONDITIONAL_MSG = '`%<current>s` returns a model which is ' \
130
- 'always truthy.'
131
-
132
- CREATE_PERSIST_METHODS = %i[create create_or_find_by
133
- first_or_create find_or_create_by].freeze
134
- MODIFY_PERSIST_METHODS = %i[save
135
- update update_attributes destroy].freeze
124
+ MSG = 'Use `%<prefer>s` instead of `%<current>s` if the return value is not checked.'
125
+ CREATE_MSG = "#{MSG} Or check `persisted?` on model returned from `%<current>s`."
126
+ CREATE_CONDITIONAL_MSG = '`%<current>s` returns a model which is always truthy.'
127
+
128
+ CREATE_PERSIST_METHODS = %i[create create_or_find_by first_or_create find_or_create_by].freeze
129
+ MODIFY_PERSIST_METHODS = %i[save update update_attributes destroy].freeze
136
130
  RESTRICT_ON_SEND = (CREATE_PERSIST_METHODS + MODIFY_PERSIST_METHODS).freeze
137
131
 
138
132
  def self.joining_forces
@@ -244,8 +238,7 @@ module RuboCop
244
238
  parent = node.parent
245
239
  return false unless parent
246
240
 
247
- operator_or_single_negative?(parent) ||
248
- (conditional?(parent) && node == parent.condition)
241
+ operator_or_single_negative?(parent) || (conditional?(parent) && node == parent.condition)
249
242
  end
250
243
 
251
244
  def operator_or_single_negative?(node)
@@ -294,9 +287,7 @@ module RuboCop
294
287
  # NameSpace::Const != ::Const
295
288
  # Const != NameSpace::Const
296
289
  def const_matches?(const, allowed_const)
297
- parts = allowed_const.split('::').reverse.zip(
298
- const.split('::').reverse
299
- )
290
+ parts = allowed_const.split('::').reverse.zip(const.split('::').reverse)
300
291
  parts.all? do |(allowed_part, const_part)|
301
292
  allowed_part == const_part.to_s
302
293
  end
@@ -335,9 +326,7 @@ module RuboCop
335
326
  end
336
327
 
337
328
  def persist_method?(node, methods = RESTRICT_ON_SEND)
338
- methods.include?(node.method_name) &&
339
- expected_signature?(node) &&
340
- !allowed_receiver?(node)
329
+ methods.include?(node.method_name) && expected_signature?(node) && !allowed_receiver?(node)
341
330
  end
342
331
 
343
332
  # Check argument signature as no arguments or one hash
@@ -345,8 +334,7 @@ module RuboCop
345
334
  !node.arguments? ||
346
335
  (node.arguments.one? &&
347
336
  node.method_name != :destroy &&
348
- (node.first_argument.hash_type? ||
349
- !node.first_argument.literal?))
337
+ (node.first_argument.hash_type? || !node.first_argument.literal?))
350
338
  end
351
339
  end
352
340
  end
@@ -44,10 +44,7 @@ module RuboCop
44
44
 
45
45
  MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
46
46
 
47
- PREFERRED_METHODS = {
48
- translate: :t,
49
- localize: :l
50
- }.freeze
47
+ PREFERRED_METHODS = { translate: :t, localize: :l }.freeze
51
48
 
52
49
  RESTRICT_ON_SEND = PREFERRED_METHODS.keys.freeze
53
50
 
@@ -93,8 +93,7 @@ module RuboCop
93
93
  end
94
94
 
95
95
  def allowed_method?(node)
96
- METHODS_WITH_ARGUMENTS.include?(node.method_name.to_s) &&
97
- !node.arguments?
96
+ METHODS_WITH_ARGUMENTS.include?(node.method_name.to_s) && !node.arguments?
98
97
  end
99
98
 
100
99
  def forbidden_methods
@@ -72,11 +72,7 @@ module RuboCop
72
72
  end
73
73
 
74
74
  def message(node)
75
- format(
76
- MSG,
77
- expect: "#{node.source}#{SQUISH}",
78
- current: node.source
79
- )
75
+ format(MSG, expect: "#{node.source}#{SQUISH}", current: node.source)
80
76
  end
81
77
  end
82
78
  end
@@ -43,21 +43,17 @@ module RuboCop
43
43
  include ConfigurableEnforcedStyle
44
44
  extend AutoCorrector
45
45
 
46
- MSG = 'Do not use `%<current>s` without zone. Use `%<prefer>s` ' \
47
- 'instead.'
46
+ MSG = 'Do not use `%<current>s` without zone. Use `%<prefer>s` instead.'
48
47
 
49
- MSG_ACCEPTABLE = 'Do not use `%<current>s` without zone. ' \
50
- 'Use one of %<prefer>s instead.'
48
+ MSG_ACCEPTABLE = 'Do not use `%<current>s` without zone. Use one of %<prefer>s instead.'
51
49
 
52
- MSG_LOCALTIME = 'Do not use `Time.localtime` without ' \
53
- 'offset or zone.'
50
+ MSG_LOCALTIME = 'Do not use `Time.localtime` without offset or zone.'
54
51
 
55
52
  GOOD_METHODS = %i[zone zone_default find_zone find_zone!].freeze
56
53
 
57
54
  DANGEROUS_METHODS = %i[now local new parse at].freeze
58
55
 
59
- ACCEPTED_METHODS = %i[in_time_zone utc getlocal xmlschema iso8601
60
- jisx0301 rfc3339 httpdate to_i to_f].freeze
56
+ ACCEPTED_METHODS = %i[in_time_zone utc getlocal xmlschema iso8601 jisx0301 rfc3339 httpdate to_i to_f].freeze
61
57
 
62
58
  TIMEZONE_SPECIFIER = /[A-z]/.freeze
63
59
 
@@ -100,13 +96,11 @@ module RuboCop
100
96
  # remove redundant `.in_time_zone` from `Time.zone.now.in_time_zone`
101
97
  def remove_redundant_in_time_zone(corrector, node)
102
98
  time_methods_called = extract_method_chain(node)
103
- return unless time_methods_called.include?(:in_time_zone) ||
104
- time_methods_called.include?(:zone)
99
+ return unless time_methods_called.include?(:in_time_zone) || time_methods_called.include?(:zone)
105
100
 
106
101
  while node&.send_type?
107
102
  if node.children.last == :in_time_zone
108
- in_time_zone_with_dot =
109
- node.loc.selector.adjust(begin_pos: -1)
103
+ in_time_zone_with_dot = node.loc.selector.adjust(begin_pos: -1)
110
104
  corrector.remove(in_time_zone_with_dot)
111
105
  end
112
106
  node = node.parent
@@ -144,9 +138,7 @@ module RuboCop
144
138
  )
145
139
  else
146
140
  safe_method_name = safe_method(method_name, node)
147
- format(MSG,
148
- current: "#{klass}.#{method_name}",
149
- prefer: "Time.zone.#{safe_method_name}")
141
+ format(MSG, current: "#{klass}.#{method_name}", prefer: "Time.zone.#{safe_method_name}")
150
142
  end
151
143
  end
152
144
 
@@ -227,10 +219,7 @@ module RuboCop
227
219
  end
228
220
 
229
221
  def acceptable_methods(klass, method_name, node)
230
- acceptable = [
231
- "`Time.zone.#{safe_method(method_name, node)}`",
232
- "`#{klass}.current`"
233
- ]
222
+ acceptable = ["`Time.zone.#{safe_method(method_name, node)}`", "`#{klass}.current`"]
234
223
 
235
224
  ACCEPTED_METHODS.each do |am|
236
225
  acceptable << "`#{klass}.#{method_name}.#{am}`"
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Identifies passing any argument to `#to_s`.
7
+ #
8
+ # @safety
9
+ # This cop is marked as unsafe because it may detect `#to_s` calls
10
+ # that are not related to Active Support implementation.
11
+ #
12
+ # @example
13
+ #
14
+ # # bad
15
+ # obj.to_s(:delimited)
16
+ #
17
+ # # good
18
+ # obj.to_formatted_s(:delimited)
19
+ #
20
+ class ToSWithArgument < Base
21
+ extend AutoCorrector
22
+ extend TargetRailsVersion
23
+
24
+ MSG = 'Use `to_formatted_s` instead.'
25
+
26
+ RESTRICT_ON_SEND = %i[to_s].freeze
27
+
28
+ minimum_target_rails_version 7.0
29
+
30
+ def on_send(node)
31
+ return if node.arguments.empty?
32
+
33
+ add_offense(node.loc.selector) do |corrector|
34
+ corrector.replace(node.loc.selector, 'to_formatted_s')
35
+ end
36
+ end
37
+ alias on_csend on_send
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Identifies top-level `HashWithIndifferentAccess`.
7
+ # This has been soft-deprecated since Rails 5.1.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # HashWithIndifferentAccess.new(foo: 'bar')
12
+ #
13
+ # # good
14
+ # ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar')
15
+ #
16
+ class TopLevelHashWithIndifferentAccess < Base
17
+ extend AutoCorrector
18
+ extend TargetRailsVersion
19
+
20
+ minimum_target_rails_version 5.1
21
+
22
+ MSG = 'Avoid top-level `HashWithIndifferentAccess`.'
23
+
24
+ # @!method top_level_hash_with_indifferent_access?(node)
25
+ # @param [RuboCop::AST::ConstNode] node
26
+ # @return [Boolean]
27
+ def_node_matcher :top_level_hash_with_indifferent_access?, <<~PATTERN
28
+ (const {nil? cbase} :HashWithIndifferentAccess)
29
+ PATTERN
30
+
31
+ # @param [RuboCop::AST::ConstNode] node
32
+ def on_const(node)
33
+ return unless top_level_hash_with_indifferent_access?(node)
34
+ return if node.parent&.class_type? && node.parent.ancestors.any?(&:module_type?)
35
+
36
+ add_offense(node) do |corrector|
37
+ autocorrect(corrector, node)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def autocorrect(corrector, node)
44
+ corrector.insert_before(node.location.name, 'ActiveSupport::')
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -54,11 +54,9 @@ module RuboCop
54
54
  NEWLINE = "\n"
55
55
  PATTERN = '[!^block (send (send %<type>s :pluck ...) :uniq ...)]'
56
56
 
57
- def_node_matcher :conservative_node_match,
58
- format(PATTERN, type: 'const')
57
+ def_node_matcher :conservative_node_match, format(PATTERN, type: 'const')
59
58
 
60
- def_node_matcher :aggressive_node_match,
61
- format(PATTERN, type: '_')
59
+ def_node_matcher :aggressive_node_match, format(PATTERN, type: '_')
62
60
 
63
61
  def on_send(node)
64
62
  uniq = if style == :conservative
@@ -80,8 +78,7 @@ module RuboCop
80
78
  private
81
79
 
82
80
  def dot_method_with_whitespace(method, node)
83
- range_between(dot_method_begin_pos(method, node),
84
- node.loc.selector.end_pos)
81
+ range_between(dot_method_begin_pos(method, node), node.loc.selector.end_pos)
85
82
  end
86
83
 
87
84
  def dot_method_begin_pos(method, node)
@@ -59,9 +59,7 @@ module RuboCop
59
59
  add_indices = schema.add_indices_by(table_name: table_name(klass))
60
60
 
61
61
  (table.indices + add_indices).any? do |index|
62
- index.unique &&
63
- (index.columns.to_set == names ||
64
- include_column_names_in_expression_index?(index, names))
62
+ index.unique && (index.columns.to_set == names || include_column_names_in_expression_index?(index, names))
65
63
  end
66
64
  end
67
65