rubocop-rails 2.20.2 → 2.24.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -7
  3. data/config/default.yml +72 -10
  4. data/lib/rubocop/cop/mixin/active_record_helper.rb +15 -3
  5. data/lib/rubocop/cop/mixin/database_type_resolvable.rb +66 -0
  6. data/lib/rubocop/cop/mixin/index_method.rb +2 -2
  7. data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +3 -1
  8. data/lib/rubocop/cop/rails/action_controller_test_case.rb +2 -2
  9. data/lib/rubocop/cop/rails/action_filter.rb +3 -0
  10. data/lib/rubocop/cop/rails/active_record_aliases.rb +2 -2
  11. data/lib/rubocop/cop/rails/active_support_aliases.rb +6 -5
  12. data/lib/rubocop/cop/rails/active_support_on_load.rb +21 -1
  13. data/lib/rubocop/cop/rails/after_commit_override.rb +1 -1
  14. data/lib/rubocop/cop/rails/bulk_change_table.rb +8 -41
  15. data/lib/rubocop/cop/rails/content_tag.rb +1 -1
  16. data/lib/rubocop/cop/rails/dangerous_column_names.rb +446 -0
  17. data/lib/rubocop/cop/rails/date.rb +1 -1
  18. data/lib/rubocop/cop/rails/duplicate_association.rb +69 -12
  19. data/lib/rubocop/cop/rails/dynamic_find_by.rb +3 -3
  20. data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +2 -2
  21. data/lib/rubocop/cop/rails/env_local.rb +46 -0
  22. data/lib/rubocop/cop/rails/expanded_date_range.rb +1 -1
  23. data/lib/rubocop/cop/rails/file_path.rb +9 -6
  24. data/lib/rubocop/cop/rails/find_by.rb +3 -3
  25. data/lib/rubocop/cop/rails/find_by_id.rb +9 -23
  26. data/lib/rubocop/cop/rails/freeze_time.rb +1 -1
  27. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +1 -1
  28. data/lib/rubocop/cop/rails/helper_instance_variable.rb +1 -1
  29. data/lib/rubocop/cop/rails/http_status.rb +4 -3
  30. data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +63 -13
  31. data/lib/rubocop/cop/rails/inquiry.rb +1 -0
  32. data/lib/rubocop/cop/rails/inverse_of.rb +1 -1
  33. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +7 -8
  34. data/lib/rubocop/cop/rails/not_null_column.rb +13 -3
  35. data/lib/rubocop/cop/rails/output.rb +3 -2
  36. data/lib/rubocop/cop/rails/pick.rb +6 -5
  37. data/lib/rubocop/cop/rails/pluck.rb +1 -1
  38. data/lib/rubocop/cop/rails/pluck_id.rb +2 -1
  39. data/lib/rubocop/cop/rails/pluck_in_where.rb +18 -5
  40. data/lib/rubocop/cop/rails/rake_environment.rb +22 -6
  41. data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +219 -0
  42. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +7 -0
  43. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +1 -1
  44. data/lib/rubocop/cop/rails/response_parsed_body.rb +52 -10
  45. data/lib/rubocop/cop/rails/reversible_migration.rb +4 -4
  46. data/lib/rubocop/cop/rails/root_pathname_methods.rb +38 -4
  47. data/lib/rubocop/cop/rails/save_bang.rb +15 -8
  48. data/lib/rubocop/cop/rails/schema_comment.rb +16 -10
  49. data/lib/rubocop/cop/rails/select_map.rb +78 -0
  50. data/lib/rubocop/cop/rails/time_zone.rb +13 -5
  51. data/lib/rubocop/cop/rails/transaction_exit_statement.rb +29 -10
  52. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +12 -4
  53. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +2 -2
  54. data/lib/rubocop/cop/rails/unknown_env.rb +5 -1
  55. data/lib/rubocop/cop/rails/unused_render_content.rb +67 -0
  56. data/lib/rubocop/cop/rails/validation.rb +2 -2
  57. data/lib/rubocop/cop/rails/where_equals.rb +3 -2
  58. data/lib/rubocop/cop/rails/where_exists.rb +9 -9
  59. data/lib/rubocop/cop/rails/where_missing.rb +6 -2
  60. data/lib/rubocop/cop/rails/where_not.rb +8 -6
  61. data/lib/rubocop/cop/rails_cops.rb +6 -0
  62. data/lib/rubocop/rails/schema_loader/schema.rb +3 -2
  63. data/lib/rubocop/rails/schema_loader.rb +5 -15
  64. data/lib/rubocop/rails/version.rb +1 -1
  65. data/lib/rubocop-rails.rb +8 -0
  66. metadata +30 -4
@@ -0,0 +1,446 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Avoid dangerous column names.
7
+ #
8
+ # Some column names are considered dangerous because they would overwrite methods already defined.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # add_column :users, :save
13
+ #
14
+ # # good
15
+ # add_column :users, :saved
16
+ class DangerousColumnNames < Base # rubocop:disable Metrics/ClassLength
17
+ COLUMN_TYPE_METHOD_NAMES = %i[
18
+ bigint
19
+ binary
20
+ blob
21
+ boolean
22
+ date
23
+ datetime
24
+ decimal
25
+ float
26
+ integer
27
+ numeric
28
+ primary_key
29
+ string
30
+ text
31
+ time
32
+ ].to_set.freeze
33
+
34
+ # Generated from `ActiveRecord::AttributeMethods.dangerous_attribute_methods` on activerecord 7.1.3.
35
+ # rubocop:disable Metrics/CollectionLiteralLength
36
+ DANGEROUS_COLUMN_NAMES = %w[
37
+ __callbacks
38
+ __id__
39
+ _assign_attribute
40
+ _assign_attributes
41
+ _before_commit_callbacks
42
+ _commit_callbacks
43
+ _committed_already_called
44
+ _create_callbacks
45
+ _create_record
46
+ _delete_row
47
+ _destroy
48
+ _destroy_callbacks
49
+ _ensure_no_duplicate_errors
50
+ _find_callbacks
51
+ _find_record
52
+ _has_attribute
53
+ _initialize_callbacks
54
+ _lock_value_for_database
55
+ _merge_attributes
56
+ _primary_key_constraints_hash
57
+ _raise_readonly_record_error
58
+ _raise_record_not_destroyed
59
+ _raise_record_not_touched_error
60
+ _read_attribute
61
+ _record_changed
62
+ _reflections
63
+ _rollback_callbacks
64
+ _run_before_commit_callbacks
65
+ _run_commit_callbacks
66
+ _run_create_callbacks
67
+ _run_destroy_callbacks
68
+ _run_find_callbacks
69
+ _run_initialize_callbacks
70
+ _run_rollback_callbacks
71
+ _run_save_callbacks
72
+ _run_touch_callbacks
73
+ _run_update_callbacks
74
+ _run_validate_callbacks
75
+ _run_validation_callbacks
76
+ _save_callbacks
77
+ _touch_callbacks
78
+ _touch_row
79
+ _trigger_destroy_callback
80
+ _trigger_update_callback
81
+ _update_callbacks
82
+ _update_record
83
+ _update_row
84
+ _validate_callbacks
85
+ _validation_callbacks
86
+ _validators
87
+ _write_attribute
88
+ []
89
+ []=
90
+ accessed_fields
91
+ add_to_transaction
92
+ aggregate_reflections
93
+ all_timestamp_attributes_in_model
94
+ allow_destroy
95
+ apply_scoping
96
+ around_save_collection_association
97
+ assign_attributes
98
+ assign_multiparameter_attributes
99
+ assign_nested_attributes_for_collection_association
100
+ assign_nested_attributes_for_one_to_one_association
101
+ assign_nested_parameter_attributes
102
+ assign_to_or_mark_for_destruction
103
+ associated_records_to_validate_or_save
104
+ association
105
+ association_cached
106
+ association_foreign_key_changed
107
+ association_instance_get
108
+ association_instance_set
109
+ association_valid
110
+ attachment_changes
111
+ attachment_reflections
112
+ attribute
113
+ attribute_aliases
114
+ attribute_before_last_save
115
+ attribute_before_type_cast
116
+ attribute_came_from_user
117
+ attribute_change
118
+ attribute_change_to_be_saved
119
+ attribute_changed
120
+ attribute_changed_in_place
121
+ attribute_for_database
122
+ attribute_for_inspect
123
+ attribute_in_database
124
+ attribute_method
125
+ attribute_method_matchers
126
+ attribute_missing
127
+ attribute_names
128
+ attribute_names_for_partial_inserts
129
+ attribute_names_for_partial_updates
130
+ attribute_names_for_serialization
131
+ attribute_present
132
+ attribute_previous_change
133
+ attribute_previously_changed
134
+ attribute_previously_was
135
+ attribute_was
136
+ attribute_will_change
137
+ attribute=
138
+ attributes
139
+ attributes_before_type_cast
140
+ attributes_for_create
141
+ attributes_for_database
142
+ attributes_for_update
143
+ attributes_in_database
144
+ attributes_with_values
145
+ attributes=
146
+ automatic_scope_inversing
147
+ becomes
148
+ before_committed
149
+ belongs_to_touch_method
150
+ broadcast_action
151
+ broadcast_action_later
152
+ broadcast_action_later_to
153
+ broadcast_action_to
154
+ broadcast_after_to
155
+ broadcast_append
156
+ broadcast_append_later
157
+ broadcast_append_later_to
158
+ broadcast_append_to
159
+ broadcast_before_to
160
+ broadcast_prepend
161
+ broadcast_prepend_later
162
+ broadcast_prepend_later_to
163
+ broadcast_prepend_to
164
+ broadcast_remove
165
+ broadcast_remove_to
166
+ broadcast_render
167
+ broadcast_render_later
168
+ broadcast_render_later_to
169
+ broadcast_render_to
170
+ broadcast_rendering_with_defaults
171
+ broadcast_replace
172
+ broadcast_replace_later
173
+ broadcast_replace_later_to
174
+ broadcast_replace_to
175
+ broadcast_target_default
176
+ broadcast_update
177
+ broadcast_update_later
178
+ broadcast_update_later_to
179
+ broadcast_update_to
180
+ build_decrypt_attribute_assignments
181
+ build_encrypt_attribute_assignments
182
+ cache_key
183
+ cache_key_with_version
184
+ cache_timestamp_format
185
+ cache_version
186
+ cache_versioning
187
+ call_reject_if
188
+ can_use_fast_cache_version
189
+ cant_modify_encrypted_attributes_when_frozen
190
+ changed
191
+ changed_attribute_names_to_save
192
+ changed_attributes
193
+ changed_for_autosave
194
+ changes
195
+ changes_applied
196
+ changes_to_save
197
+ check_record_limit
198
+ ciphertext_for
199
+ class
200
+ clear_attribute_change
201
+ clear_attribute_changes
202
+ clear_changes_information
203
+ clear_timestamp_attributes
204
+ clear_transaction_record_state
205
+ clone
206
+ collection_cache_versioning
207
+ column_for_attribute
208
+ committed
209
+ connection_handler
210
+ create_or_update
211
+ current_time_from_proper_timezone
212
+ custom_inspect_method_defined
213
+ custom_validation_context
214
+ decrement
215
+ decrypt
216
+ decrypt_attributes
217
+ decrypt_rich_texts
218
+ default_connection_handler
219
+ default_role
220
+ default_scope_override
221
+ default_scopes
222
+ default_shard
223
+ default_validation_context
224
+ defined_enums
225
+ delete
226
+ destroy
227
+ destroy_association_async_job
228
+ destroy_associations
229
+ destroy_row
230
+ destroyed
231
+ destroyed_by_association
232
+ destroyed_by_association=
233
+ dup
234
+ each_counter_cached_associations
235
+ encode_with
236
+ encrypt
237
+ encrypt_attributes
238
+ encrypt_rich_texts
239
+ encryptable_rich_texts
240
+ encrypted_attribute
241
+ encrypted_attributes
242
+ encrypted_attributes=
243
+ ensure_proper_type
244
+ errors
245
+ execute_callstack_for_multiparameter_attributes
246
+ extract_callstack_for_multiparameter_attributes
247
+ find_parameter_position
248
+ forget_attribute_assignments
249
+ format_for_inspect
250
+ freeze
251
+ from_json
252
+ frozen?
253
+ halted_callback_hook
254
+ has_attribute
255
+ has_changes_to_save
256
+ has_defer_touch_attrs
257
+ has_destroy_flag
258
+ has_encrypted_attributes
259
+ has_encrypted_rich_texts
260
+ has_transactional_callbacks
261
+ hash
262
+ id
263
+ id_before_type_cast
264
+ id_for_database
265
+ id_in_database
266
+ id_was
267
+ id=
268
+ include_root_in_json
269
+ increment
270
+ init_internals
271
+ init_with
272
+ init_with_attributes
273
+ initialize_internals_callback
274
+ inspection_filter
275
+ invalid
276
+ lock
277
+ lock_optimistically
278
+ locking_enabled
279
+ logger
280
+ mark_for_destruction
281
+ marked_for_destruction
282
+ matched_attribute_method
283
+ max_updated_column_timestamp
284
+ missing_attribute
285
+ model_name
286
+ mutations_before_last_save
287
+ mutations_from_database
288
+ nested_attributes_options
289
+ nested_records_changed_for_autosave
290
+ new_record
291
+ no_touching
292
+ normalize_reflection_attribute
293
+ partial_inserts
294
+ partial_updates
295
+ perform_validations
296
+ persisted
297
+ pk_attribute
298
+ pluralize_table_names
299
+ populate_with_current_scope_attributes
300
+ previous_changes
301
+ previously_new_record
302
+ previously_persisted
303
+ primary_key_prefix_type
304
+ query_attribute
305
+ raise_nested_attributes_record_not_found
306
+ raise_validation_error
307
+ raw_timestamp_to_cache_version
308
+ read_attribute
309
+ read_attribute_before_type_cast
310
+ read_attribute_for_serialization
311
+ read_attribute_for_validation
312
+ read_store_attribute
313
+ readonly
314
+ record_timestamps
315
+ record_timestamps=
316
+ reject_new_record
317
+ reload
318
+ remember_transaction_record_state
319
+ respond_to_without_attributes
320
+ restore_attribute
321
+ restore_attributes
322
+ restore_transaction_record_state
323
+ rolledback
324
+ run_callbacks
325
+ run_validations
326
+ sanitize_for_mass_assignment
327
+ sanitize_forbidden_attributes
328
+ save
329
+ save_belongs_to_association
330
+ save_collection_association
331
+ save_has_one_association
332
+ saved_change_to_attribute
333
+ saved_changes
334
+ serializable_add_includes
335
+ serializable_attributes
336
+ serializable_hash
337
+ should_record_timestamps
338
+ signed_id
339
+ signed_id_verifier_secret
340
+ skip_time_zone_conversion_for_attributes
341
+ slice
342
+ store_accessor_for
343
+ store_full_class_name
344
+ store_full_sti_class
345
+ strict_loaded_associations
346
+ strict_loading
347
+ strict_loading_mode
348
+ strict_loading_n_plus_one_only
349
+ surreptitiously_touch
350
+ table_name_prefix
351
+ table_name_suffix
352
+ time_zone_aware_attributes
353
+ time_zone_aware_types
354
+ timestamp_attributes_for_create_in_model
355
+ timestamp_attributes_for_update_in_model
356
+ to_ary
357
+ to_gid
358
+ to_gid_param
359
+ to_global_id
360
+ to_key
361
+ to_model
362
+ to_partial_path
363
+ to_sgid
364
+ to_sgid_param
365
+ to_signed_global_id
366
+ toggle
367
+ touch
368
+ touch_deferred_attributes
369
+ touch_later
370
+ transaction
371
+ transaction_include_any_action
372
+ trigger_transactional_callbacks
373
+ type_cast_attribute_value
374
+ type_for_attribute
375
+ update
376
+ update_attribute
377
+ update_column
378
+ update_columns
379
+ valid
380
+ validate
381
+ validate_collection_association
382
+ validate_encryption_allowed
383
+ validate_single_association
384
+ validates_absence_of
385
+ validates_acceptance_of
386
+ validates_comparison_of
387
+ validates_confirmation_of
388
+ validates_exclusion_of
389
+ validates_format_of
390
+ validates_inclusion_of
391
+ validates_length_of
392
+ validates_numericality_of
393
+ validates_presence_of
394
+ validates_size_of
395
+ validates_with
396
+ validation_context
397
+ validation_context=
398
+ values_at
399
+ verify_readonly_attribute
400
+ will_be_destroyed
401
+ will_save_change_to_attribute
402
+ with_lock
403
+ with_transaction_returning_status
404
+ write_attribute
405
+ write_store_attribute
406
+ ].freeze
407
+ # rubocop:enable Metrics/CollectionLiteralLength
408
+
409
+ MSG = 'Avoid dangerous column names.'
410
+
411
+ RESTRICT_ON_SEND = [:add_column, :rename, :rename_column, *COLUMN_TYPE_METHOD_NAMES].freeze
412
+
413
+ def on_send(node)
414
+ column_name_node = column_name_node_from(node)
415
+ return false unless column_name_node
416
+ return false unless dangerous_column_name_node?(column_name_node)
417
+
418
+ add_offense(column_name_node)
419
+ end
420
+
421
+ private
422
+
423
+ def column_name_node_from(node)
424
+ case node.method_name
425
+ when :add_column, :rename
426
+ node.arguments[1]
427
+ when :rename_column
428
+ node.arguments[2]
429
+ when *COLUMN_TYPE_METHOD_NAMES
430
+ node.first_argument
431
+ end
432
+ end
433
+
434
+ def dangerous_column_name_node?(node)
435
+ return false unless node.respond_to?(:value)
436
+
437
+ dangerous_column_name?(node.value.to_s)
438
+ end
439
+
440
+ def dangerous_column_name?(column_name)
441
+ DANGEROUS_COLUMN_NAMES.include?(column_name)
442
+ end
443
+ end
444
+ end
445
+ end
446
+ end
@@ -139,7 +139,7 @@ module RuboCop
139
139
  end
140
140
 
141
141
  def safe_to_time?(node)
142
- return unless node.method?(:to_time)
142
+ return false unless node.method?(:to_time)
143
143
 
144
144
  if node.receiver.str_type?
145
145
  zone_regexp = /([+-][\d:]+|\dZ)\z/
@@ -20,35 +20,92 @@ module RuboCop
20
20
  # belongs_to :bar
21
21
  # has_one :foo
22
22
  #
23
+ # # bad
24
+ # has_many :foo, class_name: 'Foo'
25
+ # has_many :bar, class_name: 'Foo'
26
+ # has_one :baz
27
+ #
28
+ # # good
29
+ # has_many :bar, class_name: 'Foo'
30
+ # has_one :foo
31
+ #
23
32
  class DuplicateAssociation < Base
24
33
  include RangeHelp
25
34
  extend AutoCorrector
26
35
  include ClassSendNodeHelper
36
+ include ActiveRecordHelper
27
37
 
28
38
  MSG = "Association `%<name>s` is defined multiple times. Don't repeat associations."
39
+ MSG_CLASS_NAME = "Association `class_name: %<name>s` is defined multiple times. Don't repeat associations."
29
40
 
30
41
  def_node_matcher :association, <<~PATTERN
31
- (send nil? {:belongs_to :has_one :has_many :has_and_belongs_to_many} ({sym str} $_) ...)
42
+ (send nil? {:belongs_to :has_one :has_many :has_and_belongs_to_many} ({sym str} $_) $...)
43
+ PATTERN
44
+
45
+ def_node_matcher :class_name, <<~PATTERN
46
+ (hash (pair (sym :class_name) $_))
32
47
  PATTERN
33
48
 
34
49
  def on_class(class_node)
35
- offenses(class_node).each do |name, nodes|
36
- nodes.each do |node|
37
- add_offense(node, message: format(MSG, name: name)) do |corrector|
38
- next if same_line?(nodes.last, node)
50
+ return unless active_record?(class_node.parent_class)
39
51
 
40
- corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
41
- end
42
- end
52
+ association_nodes = association_nodes(class_node)
53
+
54
+ duplicated_association_name_nodes(association_nodes).each do |name, nodes|
55
+ register_offense(name, nodes, MSG)
56
+ end
57
+
58
+ duplicated_class_name_nodes(association_nodes).each do |class_name, nodes|
59
+ register_offense(class_name, nodes, MSG_CLASS_NAME)
43
60
  end
44
61
  end
45
62
 
46
63
  private
47
64
 
48
- def offenses(class_node)
49
- class_send_nodes(class_node).select { |node| association(node) }
50
- .group_by { |node| association(node).to_sym }
51
- .select { |_, nodes| nodes.length > 1 }
65
+ def register_offense(name, nodes, message_template)
66
+ nodes.each do |node|
67
+ add_offense(node, message: format(message_template, name: name)) do |corrector|
68
+ next if same_line?(nodes.last, node)
69
+
70
+ corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
71
+ end
72
+ end
73
+ end
74
+
75
+ def association_nodes(class_node)
76
+ class_send_nodes(class_node).select do |node|
77
+ association(node)&.first
78
+ end
79
+ end
80
+
81
+ def duplicated_association_name_nodes(association_nodes)
82
+ grouped_associations = association_nodes.group_by do |node|
83
+ association(node).first.to_sym
84
+ end
85
+
86
+ leave_duplicated_association(grouped_associations)
87
+ end
88
+
89
+ def duplicated_class_name_nodes(association_nodes)
90
+ filtered_nodes = association_nodes.reject { |node| node.method?(:belongs_to) }
91
+ grouped_associations = filtered_nodes.group_by do |node|
92
+ arguments = association(node).last
93
+ next unless arguments.count == 1
94
+
95
+ if (class_name = class_name(arguments.first))
96
+ class_name.source
97
+ end
98
+ end
99
+
100
+ grouped_associations.delete(nil)
101
+
102
+ leave_duplicated_association(grouped_associations)
103
+ end
104
+
105
+ def leave_duplicated_association(grouped_associations)
106
+ grouped_associations.select do |_, nodes|
107
+ nodes.length > 1
108
+ end
52
109
  end
53
110
  end
54
111
  end
@@ -74,13 +74,13 @@ module RuboCop
74
74
  end
75
75
 
76
76
  def allowed_method?(node)
77
- return unless cop_config['AllowedMethods']
77
+ return false unless cop_config['AllowedMethods']
78
78
 
79
79
  cop_config['AllowedMethods'].include?(node.method_name.to_s)
80
80
  end
81
81
 
82
82
  def allowed_receiver?(node)
83
- return unless cop_config['AllowedReceivers'] && node.receiver
83
+ return false unless cop_config['AllowedReceivers'] && node.receiver
84
84
 
85
85
  cop_config['AllowedReceivers'].include?(node.receiver.source)
86
86
  end
@@ -88,7 +88,7 @@ module RuboCop
88
88
  # config option `WhiteList` will be deprecated soon
89
89
  def whitelisted?(node)
90
90
  whitelist_config = cop_config['Whitelist']
91
- return unless whitelist_config
91
+ return false unless whitelist_config
92
92
 
93
93
  whitelist_config.include?(node.method_name.to_s)
94
94
  end
@@ -14,10 +14,10 @@ module RuboCop
14
14
  # when no output would be produced anyway.
15
15
  #
16
16
  # @example
17
- # #bad
17
+ # # bad
18
18
  # Rails.logger.debug "The time is #{Time.zone.now}."
19
19
  #
20
- # #good
20
+ # # good
21
21
  # Rails.logger.debug { "The time is #{Time.zone.now}." }
22
22
  #
23
23
  class EagerEvaluationLogMessage < Base
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Checks for usage of `Rails.env.development? || Rails.env.test?` which
7
+ # can be replaced with `Rails.env.local?`, introduced in Rails 7.1.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # Rails.env.development? || Rails.env.test?
13
+ #
14
+ # # good
15
+ # Rails.env.local?
16
+ #
17
+ class EnvLocal < Base
18
+ extend AutoCorrector
19
+ extend TargetRailsVersion
20
+
21
+ MSG = 'Use `Rails.env.local?` instead.'
22
+ LOCAL_ENVIRONMENTS = %i[development? test?].to_set.freeze
23
+
24
+ minimum_target_rails_version 7.1
25
+
26
+ # @!method rails_env_local_candidate?(node)
27
+ def_node_matcher :rails_env_local_candidate?, <<~PATTERN
28
+ (or
29
+ (send (send (const {cbase nil? } :Rails) :env) $%LOCAL_ENVIRONMENTS)
30
+ (send (send (const {cbase nil? } :Rails) :env) $%LOCAL_ENVIRONMENTS)
31
+ )
32
+ PATTERN
33
+
34
+ def on_or(node)
35
+ rails_env_local_candidate?(node) do |*environments|
36
+ next unless environments.to_set == LOCAL_ENVIRONMENTS
37
+
38
+ add_offense(node) do |corrector|
39
+ corrector.replace(node, 'Rails.env.local?')
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -51,7 +51,7 @@ module RuboCop
51
51
  return if allow?(begin_node, end_node)
52
52
 
53
53
  preferred_method = preferred_method(begin_node)
54
- if begin_node.method?(:beginning_of_week) && begin_node.arguments.one?
54
+ if begin_node.method?(:beginning_of_week) && begin_node.arguments.one? && end_node.arguments.one?
55
55
  return unless same_argument?(begin_node, end_node)
56
56
 
57
57
  preferred_method << "(#{begin_node.first_argument.source})"