rubocop-rails 2.21.2 → 2.23.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -10
  3. data/config/default.yml +11 -3
  4. data/lib/rubocop/cop/mixin/active_record_helper.rb +9 -2
  5. data/lib/rubocop/cop/mixin/database_type_resolvable.rb +66 -0
  6. data/lib/rubocop/cop/rails/action_filter.rb +3 -0
  7. data/lib/rubocop/cop/rails/active_record_aliases.rb +2 -2
  8. data/lib/rubocop/cop/rails/active_support_aliases.rb +6 -5
  9. data/lib/rubocop/cop/rails/after_commit_override.rb +1 -1
  10. data/lib/rubocop/cop/rails/bulk_change_table.rb +6 -56
  11. data/lib/rubocop/cop/rails/content_tag.rb +1 -1
  12. data/lib/rubocop/cop/rails/dangerous_column_names.rb +10 -2
  13. data/lib/rubocop/cop/rails/duplicate_association.rb +66 -12
  14. data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +2 -2
  15. data/lib/rubocop/cop/rails/env_local.rb +46 -0
  16. data/lib/rubocop/cop/rails/file_path.rb +5 -5
  17. data/lib/rubocop/cop/rails/find_by.rb +2 -2
  18. data/lib/rubocop/cop/rails/find_by_id.rb +9 -23
  19. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +1 -1
  20. data/lib/rubocop/cop/rails/helper_instance_variable.rb +1 -1
  21. data/lib/rubocop/cop/rails/inquiry.rb +1 -0
  22. data/lib/rubocop/cop/rails/inverse_of.rb +1 -1
  23. data/lib/rubocop/cop/rails/not_null_column.rb +13 -3
  24. data/lib/rubocop/cop/rails/output.rb +3 -2
  25. data/lib/rubocop/cop/rails/pick.rb +6 -5
  26. data/lib/rubocop/cop/rails/pluck.rb +1 -1
  27. data/lib/rubocop/cop/rails/pluck_id.rb +2 -1
  28. data/lib/rubocop/cop/rails/pluck_in_where.rb +18 -5
  29. data/lib/rubocop/cop/rails/rake_environment.rb +2 -2
  30. data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +53 -2
  31. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +7 -0
  32. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +1 -1
  33. data/lib/rubocop/cop/rails/response_parsed_body.rb +52 -10
  34. data/lib/rubocop/cop/rails/reversible_migration.rb +2 -2
  35. data/lib/rubocop/cop/rails/save_bang.rb +11 -6
  36. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +1 -1
  37. data/lib/rubocop/cop/rails/unknown_env.rb +5 -1
  38. data/lib/rubocop/cop/rails/validation.rb +2 -2
  39. data/lib/rubocop/cop/rails/where_equals.rb +3 -2
  40. data/lib/rubocop/cop/rails/where_exists.rb +9 -8
  41. data/lib/rubocop/cop/rails/where_missing.rb +1 -1
  42. data/lib/rubocop/cop/rails/where_not.rb +8 -6
  43. data/lib/rubocop/cop/rails_cops.rb +2 -0
  44. data/lib/rubocop/rails/schema_loader/schema.rb +2 -2
  45. data/lib/rubocop/rails/version.rb +1 -1
  46. metadata +26 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e154e69d10b43226db08e454d992d724dc7504d54cac804e597156ee5cf13e5
4
- data.tar.gz: 60d4697e076620a134f48eff2f81be7b2004fd824796b1a1b15f5822239e3ce3
3
+ metadata.gz: f90e14aba3397c0edacdc63f7dd9d5b83ed7d79382041f5f37e3201dea96c3db
4
+ data.tar.gz: 31ebefe705b4148901128b2b5feb5948b737acdd5fae210f8da6cec845f17023
5
5
  SHA512:
6
- metadata.gz: f0aa0f69a7aa3b90d1e402ab26bdf5a4d03680080d9b06ba157cd92f3c5c290f821e81d4a99636009e17ec4ea2de0cfd405cea00134ebccd4323d424fb790ec8
7
- data.tar.gz: 6b6ddbe47c1b112638cc3693464d9509f589a6683317b0bb30925a27788e39351a3a10853ecf286b419d3ec3b17e13aee9fd09ae07581d209635f0a56a44065b
6
+ metadata.gz: b90a96dae0688c93975260b480c2add1449c5ca21503a071453464f63ba030713501e1d20e2ea4fdf3385a58d1cf71003e2ede718ec9b6aa80e86e4a82be00a7
7
+ data.tar.gz: ad4ed3bbe86f2d1179766c512f347aaa21ea6e18e8af5a00f07eb20098c6c3c0103955ba4366bf952528773b7b2ff6a21af52369969227fd05233138a1060708
data/README.md CHANGED
@@ -56,6 +56,8 @@ Note: `--rails` option is required while `rubocop` command supports `--rails` op
56
56
  ### Rake task
57
57
 
58
58
  ```ruby
59
+ require 'rubocop/rake_task'
60
+
59
61
  RuboCop::RakeTask.new do |task|
60
62
  task.requires << 'rubocop-rails'
61
63
  end
@@ -64,23 +66,21 @@ end
64
66
  ## Rails configuration tip
65
67
 
66
68
  If you are using Rails 6.1 or newer, add the following `config.generators.after_generate` setting to
67
- your config/application.rb to apply RuboCop autocorrection to code generated by `bin/rails g`.
69
+ your `config/environments/development.rb` to apply RuboCop autocorrection to code generated by `bin/rails g`.
68
70
 
69
71
  ```ruby
70
- # config/application.rb
71
- module YourCoolApp
72
- class Application < Rails::Application
73
- config.generators.after_generate do |files|
74
- parsable_files = files.filter { |file| file.end_with?('.rb') }
75
- unless parsable_files.empty?
76
- system("bundle exec rubocop -A --fail-level=E #{parsable_files.shelljoin}", exception: true)
77
- end
72
+ # config/environments/development.rb
73
+ Rails.application.configure do
74
+ config.generators.after_generate do |files|
75
+ parsable_files = files.filter { |file| file.end_with?('.rb') }
76
+ unless parsable_files.empty?
77
+ system("bundle exec rubocop -A --fail-level=E #{parsable_files.shelljoin}", exception: true)
78
78
  end
79
79
  end
80
80
  end
81
81
  ```
82
82
 
83
- It uses `rubocop -A` to apply `Style/FrozenStringLiteralComment` and other unsafe autocorretion cops.
83
+ It uses `rubocop -A` to apply `Style/FrozenStringLiteralComment` and other unsafe autocorrection cops.
84
84
  `rubocop -A` is unsafe autocorrection, but code generated by default is simple and less likely to
85
85
  be incompatible with `rubocop -A`. If you have problems you can replace it with `rubocop -a` instead.
86
86
 
data/config/default.yml CHANGED
@@ -95,8 +95,9 @@ Rails/ActionControllerTestCase:
95
95
 
96
96
  Rails/ActionFilter:
97
97
  Description: 'Enforces consistent use of action filter methods.'
98
- Enabled: true
98
+ Enabled: false
99
99
  VersionAdded: '0.19'
100
+ VersionChanged: '2.22'
100
101
  EnforcedStyle: action
101
102
  SupportedStyles:
102
103
  - action
@@ -338,7 +339,6 @@ Rails/Date:
338
339
 
339
340
  Rails/DefaultScope:
340
341
  Description: 'Avoid use of `default_scope`.'
341
- StyleGuide: 'https://rails.rubystyle.guide#avoid-default-scope'
342
342
  Enabled: false
343
343
  VersionAdded: '2.7'
344
344
 
@@ -430,6 +430,11 @@ Rails/EnumUniqueness:
430
430
  Include:
431
431
  - app/models/**/*.rb
432
432
 
433
+ Rails/EnvLocal:
434
+ Description: 'Use `Rails.env.local?` instead of `Rails.env.development? || Rails.env.test?`.'
435
+ Enabled: pending
436
+ VersionAdded: '2.22'
437
+
433
438
  Rails/EnvironmentComparison:
434
439
  Description: "Favor `Rails.env.production?` over `Rails.env == 'production'`."
435
440
  Enabled: true
@@ -690,6 +695,9 @@ Rails/NotNullColumn:
690
695
  Enabled: true
691
696
  VersionAdded: '0.43'
692
697
  VersionChanged: '2.20'
698
+ Database: null
699
+ SupportedDatabases:
700
+ - mysql
693
701
  Include:
694
702
  - db/**/*.rb
695
703
 
@@ -894,7 +902,7 @@ Rails/RequireDependency:
894
902
  VersionAdded: '2.10'
895
903
 
896
904
  Rails/ResponseParsedBody:
897
- Description: Prefer `response.parsed_body` to `JSON.parse(response.body)`.
905
+ Description: Prefer `response.parsed_body` to custom parsing logic for `response.body`.
898
906
  Enabled: pending
899
907
  Safe: false
900
908
  VersionAdded: '2.18'
@@ -98,8 +98,15 @@ module RuboCop
98
98
  end
99
99
 
100
100
  def in_where?(node)
101
- send_node = node.each_ancestor(:send).first
102
- send_node && WHERE_METHODS.include?(send_node.method_name)
101
+ send_node = node.each_ancestor(:send, :csend).first
102
+ return false unless send_node
103
+
104
+ return true if WHERE_METHODS.include?(send_node.method_name)
105
+
106
+ receiver = send_node.receiver
107
+ return false unless receiver&.send_type?
108
+
109
+ send_node.method?(:not) && WHERE_METHODS.include?(receiver.method_name)
103
110
  end
104
111
  end
105
112
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # A mixin to extend cops in order to determine the database type.
6
+ #
7
+ # This module automatically detect an adapter from `development` environment
8
+ # in `config/database.yml` or the environment variable `DATABASE_URL`
9
+ # when the `Database` option is not set.
10
+ module DatabaseTypeResolvable
11
+ MYSQL = 'mysql'
12
+ POSTGRESQL = 'postgresql'
13
+
14
+ def database
15
+ cop_config['Database'] || database_from_yaml || database_from_env
16
+ end
17
+
18
+ private
19
+
20
+ def database_from_yaml
21
+ return unless database_yaml
22
+
23
+ case database_adapter
24
+ when 'mysql2', 'trilogy'
25
+ MYSQL
26
+ when 'postgresql', 'postgis'
27
+ POSTGRESQL
28
+ end
29
+ end
30
+
31
+ def database_from_env
32
+ url = ENV['DATABASE_URL'].presence
33
+ return unless url
34
+
35
+ case url
36
+ when %r{\A(mysql2|trilogy)://}
37
+ MYSQL
38
+ when %r{\Apostgres(ql)?://}
39
+ POSTGRESQL
40
+ end
41
+ end
42
+
43
+ def database_yaml
44
+ return unless File.exist?('config/database.yml')
45
+
46
+ yaml = if YAML.respond_to?(:unsafe_load_file)
47
+ YAML.unsafe_load_file('config/database.yml')
48
+ else
49
+ YAML.load_file('config/database.yml')
50
+ end
51
+ return unless yaml.is_a? Hash
52
+
53
+ config = yaml['development']
54
+ return unless config.is_a?(Hash)
55
+
56
+ config
57
+ rescue Psych::SyntaxError
58
+ # noop
59
+ end
60
+
61
+ def database_adapter
62
+ database_yaml['adapter'] || database_yaml.first.last['adapter']
63
+ end
64
+ end
65
+ end
66
+ end
@@ -8,6 +8,9 @@ module RuboCop
8
8
  # The cop is configurable and can enforce the use of the older
9
9
  # something_filter methods or the newer something_action methods.
10
10
  #
11
+ # IMPORTANT: This cop is deprecated. Because the `*_filter` methods were removed in Rails 4.2,
12
+ # and that Rails version is no longer supported by RuboCop Rails. This cop will be removed in RuboCop Rails 3.0.
13
+ #
11
14
  # @example EnforcedStyle: action (default)
12
15
  # # bad
13
16
  # after_filter :do_stuff
@@ -11,10 +11,10 @@ module RuboCop
11
11
  # `update` but the method name remained same in the method definition.
12
12
  #
13
13
  # @example
14
- # #bad
14
+ # # bad
15
15
  # book.update_attributes!(author: 'Alice')
16
16
  #
17
- # #good
17
+ # # good
18
18
  # book.update!(author: 'Alice')
19
19
  class ActiveRecordAliases < Base
20
20
  extend AutoCorrector
@@ -27,13 +27,13 @@ module RuboCop
27
27
 
28
28
  ALIASES = {
29
29
  starts_with?: {
30
- original: :start_with?, matcher: '(send str :starts_with? _)'
30
+ original: :start_with?, matcher: '(call str :starts_with? _)'
31
31
  },
32
32
  ends_with?: {
33
- original: :end_with?, matcher: '(send str :ends_with? _)'
33
+ original: :end_with?, matcher: '(call str :ends_with? _)'
34
34
  },
35
- append: { original: :<<, matcher: '(send array :append _)' },
36
- prepend: { original: :unshift, matcher: '(send array :prepend _)' }
35
+ append: { original: :<<, matcher: '(call array :append _)' },
36
+ prepend: { original: :unshift, matcher: '(call array :prepend _)' }
37
37
  }.freeze
38
38
 
39
39
  ALIASES.each do |aliased_method, options|
@@ -47,13 +47,14 @@ module RuboCop
47
47
  preferred_method = ALIASES[aliased_method][:original]
48
48
  message = format(MSG, prefer: preferred_method, current: aliased_method)
49
49
 
50
- add_offense(node, message: message) do |corrector|
50
+ add_offense(node.loc.selector.join(node.source_range.end), message: message) do |corrector|
51
51
  next if append(node)
52
52
 
53
53
  corrector.replace(node.loc.selector, preferred_method)
54
54
  end
55
55
  end
56
56
  end
57
+ alias on_csend on_send
57
58
  end
58
59
  end
59
60
  end
@@ -48,7 +48,7 @@ module RuboCop
48
48
  seen_callback_names = {}
49
49
 
50
50
  each_after_commit_callback(class_node) do |node|
51
- callback_name = node.arguments[0].value
51
+ callback_name = node.first_argument.value
52
52
  if seen_callback_names.key?(callback_name)
53
53
  add_offense(node, message: format(MSG, name: callback_name))
54
54
  else
@@ -14,7 +14,7 @@ module RuboCop
14
14
  # automatically detect an adapter from `development` environment
15
15
  # in `config/database.yml` or the environment variable `DATABASE_URL`
16
16
  # when the `Database` option is not set.
17
- # If the adapter is not `mysql2` or `postgresql`,
17
+ # If the adapter is not `mysql2`, `trilogy`, `postgresql`, or `postgis`,
18
18
  # this Cop ignores offenses.
19
19
  #
20
20
  # @example
@@ -64,6 +64,8 @@ module RuboCop
64
64
  # end
65
65
  # end
66
66
  class BulkChangeTable < Base
67
+ include DatabaseTypeResolvable
68
+
67
69
  MSG_FOR_CHANGE_TABLE = <<~MSG.chomp
68
70
  You can combine alter queries using `bulk: true` options.
69
71
  MSG
@@ -71,9 +73,6 @@ module RuboCop
71
73
  You can use `change_table :%<table>s, bulk: true` to combine alter queries.
72
74
  MSG
73
75
 
74
- MYSQL = 'mysql'
75
- POSTGRESQL = 'postgresql'
76
-
77
76
  MIGRATION_METHODS = %i[change up down].freeze
78
77
 
79
78
  COMBINABLE_TRANSFORMATIONS = %i[
@@ -175,55 +174,6 @@ module RuboCop
175
174
  options.hash_type? && options.keys.any? { |key| key.sym_type? && key.value == :bulk }
176
175
  end
177
176
 
178
- def database
179
- cop_config['Database'] || database_from_yaml || database_from_env
180
- end
181
-
182
- def database_from_yaml
183
- return nil unless database_yaml
184
-
185
- case database_adapter
186
- when 'mysql2'
187
- MYSQL
188
- when 'postgresql'
189
- POSTGRESQL
190
- end
191
- end
192
-
193
- def database_adapter
194
- database_yaml['adapter'] || database_yaml.first.last['adapter']
195
- end
196
-
197
- def database_yaml
198
- return nil unless File.exist?('config/database.yml')
199
-
200
- yaml = if YAML.respond_to?(:unsafe_load_file)
201
- YAML.unsafe_load_file('config/database.yml')
202
- else
203
- YAML.load_file('config/database.yml')
204
- end
205
- return nil unless yaml.is_a? Hash
206
-
207
- config = yaml['development']
208
- return nil unless config.is_a?(Hash)
209
-
210
- config
211
- rescue Psych::SyntaxError
212
- nil
213
- end
214
-
215
- def database_from_env
216
- url = ENV['DATABASE_URL'].presence
217
- return nil unless url
218
-
219
- case url
220
- when %r{\Amysql2://}
221
- MYSQL
222
- when %r{\Apostgres(ql)?://}
223
- POSTGRESQL
224
- end
225
- end
226
-
227
177
  def support_bulk_alter?
228
178
  case database
229
179
  when MYSQL
@@ -262,7 +212,7 @@ module RuboCop
262
212
  # @param node [RuboCop::AST::SendNode]
263
213
  def add_offense_for_alter_methods(node)
264
214
  # arguments: [{(sym :table)(str "table")} ...]
265
- table_node = node.arguments[0]
215
+ table_node = node.first_argument
266
216
  return unless table_node.is_a? RuboCop::AST::BasicLiteralNode
267
217
 
268
218
  message = format(MSG_FOR_ALTER_METHODS, table: table_node.value)
@@ -284,10 +234,10 @@ module RuboCop
284
234
  # @param new_node [RuboCop::AST::SendNode]
285
235
  def process(new_node)
286
236
  # arguments: [{(sym :table)(str "table")} ...]
287
- table_node = new_node.arguments[0]
237
+ table_node = new_node.first_argument
288
238
  if table_node.is_a? RuboCop::AST::BasicLiteralNode
289
239
  flush unless @nodes.all? do |node|
290
- node.arguments[0].value.to_s == table_node.value.to_s
240
+ node.first_argument.value.to_s == table_node.value.to_s
291
241
  end
292
242
  @nodes << new_node
293
243
  else
@@ -7,7 +7,7 @@ module RuboCop
7
7
  #
8
8
  # NOTE: Allow `tag` when the first argument is a variable because
9
9
  # `tag(name)` is simpler rather than `tag.public_send(name)`.
10
- # And this cop will be renamed to something like `LegacyTag` in the future. (e.g. RuboCop Rails 2.0)
10
+ # And this cop will be renamed to something like `LegacyTag` in the future. (e.g. RuboCop Rails 3.0)
11
11
  #
12
12
  # @example
13
13
  # # bad
@@ -31,10 +31,11 @@ module RuboCop
31
31
  time
32
32
  ].to_set.freeze
33
33
 
34
- # Generated from `ActiveRecord::AttributeMethods.dangerous_attribute_methods` on activerecord 7.0.5.
34
+ # Generated from `ActiveRecord::AttributeMethods.dangerous_attribute_methods` on activerecord 7.1.0.
35
35
  # rubocop:disable Metrics/CollectionLiteralLength
36
36
  DANGEROUS_COLUMN_NAMES = %w[
37
37
  __callbacks
38
+ __id__
38
39
  _assign_attribute
39
40
  _assign_attributes
40
41
  _before_commit_callbacks
@@ -195,11 +196,13 @@ module RuboCop
195
196
  changes_to_save
196
197
  check_record_limit
197
198
  ciphertext_for
199
+ class
198
200
  clear_attribute_change
199
201
  clear_attribute_changes
200
202
  clear_changes_information
201
203
  clear_timestamp_attributes
202
204
  clear_transaction_record_state
205
+ clone
203
206
  collection_cache_versioning
204
207
  column_for_attribute
205
208
  committed
@@ -227,6 +230,7 @@ module RuboCop
227
230
  destroyed
228
231
  destroyed_by_association
229
232
  destroyed_by_association=
233
+ dup
230
234
  each_counter_cached_associations
231
235
  encode_with
232
236
  encrypt
@@ -243,7 +247,9 @@ module RuboCop
243
247
  find_parameter_position
244
248
  forget_attribute_assignments
245
249
  format_for_inspect
250
+ freeze
246
251
  from_json
252
+ frozen?
247
253
  halted_callback_hook
248
254
  has_attribute
249
255
  has_changes_to_save
@@ -252,6 +258,7 @@ module RuboCop
252
258
  has_encrypted_attributes
253
259
  has_encrypted_rich_texts
254
260
  has_transactional_callbacks
261
+ hash
255
262
  id
256
263
  id_before_type_cast
257
264
  id_for_database
@@ -283,6 +290,7 @@ module RuboCop
283
290
  new_record
284
291
  no_touching
285
292
  normalize_reflection_attribute
293
+ object_id
286
294
  partial_inserts
287
295
  partial_updates
288
296
  perform_validations
@@ -420,7 +428,7 @@ module RuboCop
420
428
  when :rename_column
421
429
  node.arguments[2]
422
430
  when *COLUMN_TYPE_METHOD_NAMES
423
- node.arguments[0]
431
+ node.first_argument
424
432
  end
425
433
  end
426
434
 
@@ -20,6 +20,15 @@ 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
@@ -27,31 +36,76 @@ module RuboCop
27
36
  include ActiveRecordHelper
28
37
 
29
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."
30
40
 
31
41
  def_node_matcher :association, <<~PATTERN
32
- (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) $_))
33
47
  PATTERN
34
48
 
35
49
  def on_class(class_node)
36
50
  return unless active_record?(class_node.parent_class)
37
51
 
38
- offenses(class_node).each do |name, nodes|
39
- nodes.each do |node|
40
- add_offense(node, message: format(MSG, name: name)) do |corrector|
41
- next if same_line?(nodes.last, node)
52
+ association_nodes = association_nodes(class_node)
42
53
 
43
- corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
44
- end
45
- end
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)
46
60
  end
47
61
  end
48
62
 
49
63
  private
50
64
 
51
- def offenses(class_node)
52
- class_send_nodes(class_node).select { |node| association(node) }
53
- .group_by { |node| association(node).to_sym }
54
- .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
55
109
  end
56
110
  end
57
111
  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
@@ -163,9 +163,9 @@ module RuboCop
163
163
 
164
164
  def autocorrect_extension_after_rails_root_join_in_dstr(corrector, node, rails_root_index, extension_node)
165
165
  rails_root_node = node.children[rails_root_index].children.first
166
- return unless rails_root_node.arguments.last.str_type?
166
+ return unless rails_root_node.last_argument.str_type?
167
167
 
168
- corrector.insert_before(rails_root_node.arguments.last.location.end, extension_node.source)
168
+ corrector.insert_before(rails_root_node.last_argument.location.end, extension_node.source)
169
169
  corrector.remove(extension_node)
170
170
  end
171
171
 
@@ -174,7 +174,7 @@ module RuboCop
174
174
  corrector.remove(
175
175
  range_with_surrounding_space(
176
176
  range_with_surrounding_comma(
177
- node.arguments.first.source_range,
177
+ node.first_argument.source_range,
178
178
  :right
179
179
  ),
180
180
  side: :right
@@ -187,7 +187,7 @@ module RuboCop
187
187
  end
188
188
 
189
189
  def autocorrect_rails_root_join_with_string_arguments(corrector, node)
190
- corrector.replace(node.arguments.first, %("#{node.arguments.map(&:value).join('/')}"))
190
+ corrector.replace(node.first_argument, %("#{node.arguments.map(&:value).join('/')}"))
191
191
  node.arguments[1..].each do |argument|
192
192
  corrector.remove(
193
193
  range_with_surrounding_comma(
@@ -221,7 +221,7 @@ module RuboCop
221
221
  end
222
222
 
223
223
  def append_argument(corrector, node, argument_source)
224
- corrector.insert_after(node.arguments.last, %(, "#{argument_source}"))
224
+ corrector.insert_after(node.last_argument, %(, "#{argument_source}"))
225
225
  end
226
226
 
227
227
  def replace_with_rails_root_join(corrector, node, argument_source)
@@ -28,7 +28,7 @@ module RuboCop
28
28
  include RangeHelp
29
29
  extend AutoCorrector
30
30
 
31
- MSG = 'Use `find_by` instead of `where.%<method>s`.'
31
+ MSG = 'Use `find_by` instead of `where%<dot>s%<method>s`.'
32
32
  RESTRICT_ON_SEND = %i[first take].freeze
33
33
 
34
34
  def on_send(node)
@@ -37,7 +37,7 @@ module RuboCop
37
37
 
38
38
  range = offense_range(node)
39
39
 
40
- add_offense(range, message: format(MSG, method: node.method_name)) do |corrector|
40
+ add_offense(range, message: format(MSG, dot: node.loc.dot.source, method: node.method_name)) do |corrector|
41
41
  autocorrect(corrector, node)
42
42
  end
43
43
  end