rubocop-rails 2.21.2 → 2.23.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -10
- data/config/default.yml +11 -3
- data/lib/rubocop/cop/mixin/active_record_helper.rb +9 -2
- data/lib/rubocop/cop/mixin/database_type_resolvable.rb +66 -0
- data/lib/rubocop/cop/rails/action_filter.rb +3 -0
- data/lib/rubocop/cop/rails/active_record_aliases.rb +2 -2
- data/lib/rubocop/cop/rails/active_support_aliases.rb +6 -5
- data/lib/rubocop/cop/rails/after_commit_override.rb +1 -1
- data/lib/rubocop/cop/rails/bulk_change_table.rb +6 -56
- data/lib/rubocop/cop/rails/content_tag.rb +1 -1
- data/lib/rubocop/cop/rails/dangerous_column_names.rb +10 -2
- data/lib/rubocop/cop/rails/duplicate_association.rb +66 -12
- data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +2 -2
- data/lib/rubocop/cop/rails/env_local.rb +46 -0
- data/lib/rubocop/cop/rails/file_path.rb +5 -5
- data/lib/rubocop/cop/rails/find_by.rb +2 -2
- data/lib/rubocop/cop/rails/find_by_id.rb +9 -23
- 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/inquiry.rb +1 -0
- data/lib/rubocop/cop/rails/inverse_of.rb +1 -1
- data/lib/rubocop/cop/rails/not_null_column.rb +13 -3
- data/lib/rubocop/cop/rails/output.rb +3 -2
- data/lib/rubocop/cop/rails/pick.rb +6 -5
- data/lib/rubocop/cop/rails/pluck.rb +1 -1
- data/lib/rubocop/cop/rails/pluck_id.rb +2 -1
- data/lib/rubocop/cop/rails/pluck_in_where.rb +18 -5
- data/lib/rubocop/cop/rails/rake_environment.rb +2 -2
- data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +53 -2
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +7 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +1 -1
- data/lib/rubocop/cop/rails/response_parsed_body.rb +52 -10
- data/lib/rubocop/cop/rails/reversible_migration.rb +2 -2
- data/lib/rubocop/cop/rails/save_bang.rb +11 -6
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +1 -1
- data/lib/rubocop/cop/rails/unknown_env.rb +5 -1
- data/lib/rubocop/cop/rails/validation.rb +2 -2
- data/lib/rubocop/cop/rails/where_equals.rb +3 -2
- data/lib/rubocop/cop/rails/where_exists.rb +9 -8
- data/lib/rubocop/cop/rails/where_missing.rb +1 -1
- data/lib/rubocop/cop/rails/where_not.rb +8 -6
- data/lib/rubocop/cop/rails_cops.rb +2 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +2 -2
- data/lib/rubocop/rails/version.rb +1 -1
- metadata +26 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f90e14aba3397c0edacdc63f7dd9d5b83ed7d79382041f5f37e3201dea96c3db
|
4
|
+
data.tar.gz: 31ebefe705b4148901128b2b5feb5948b737acdd5fae210f8da6cec845f17023
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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/
|
69
|
+
your `config/environments/development.rb` to apply RuboCop autocorrection to code generated by `bin/rails g`.
|
68
70
|
|
69
71
|
```ruby
|
70
|
-
# config/
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
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:
|
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 `
|
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
|
-
|
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: '(
|
30
|
+
original: :start_with?, matcher: '(call str :starts_with? _)'
|
31
31
|
},
|
32
32
|
ends_with?: {
|
33
|
-
original: :end_with?, matcher: '(
|
33
|
+
original: :end_with?, matcher: '(call str :ends_with? _)'
|
34
34
|
},
|
35
|
-
append: { original: :<<, matcher: '(
|
36
|
-
prepend: { original: :unshift, matcher: '(
|
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.
|
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 `
|
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.
|
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.
|
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.
|
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
|
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.
|
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.
|
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
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
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
|
52
|
-
|
53
|
-
|
54
|
-
|
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.
|
166
|
+
return unless rails_root_node.last_argument.str_type?
|
167
167
|
|
168
|
-
corrector.insert_before(rails_root_node.
|
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.
|
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.
|
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.
|
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
|
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
|