rubocop-rails 2.25.0 → 2.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +17 -3
- data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +5 -5
- data/lib/rubocop/cop/rails/bulk_change_table.rb +10 -4
- data/lib/rubocop/cop/rails/compact_blank.rb +25 -8
- data/lib/rubocop/cop/rails/date.rb +2 -2
- data/lib/rubocop/cop/rails/enum_hash.rb +31 -8
- data/lib/rubocop/cop/rails/enum_syntax.rb +126 -0
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +29 -7
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +1 -1
- data/lib/rubocop/cop/rails/link_to_blank.rb +2 -2
- data/lib/rubocop/cop/rails/not_null_column.rb +2 -0
- data/lib/rubocop/cop/rails/pluck_in_where.rb +17 -8
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +29 -15
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +9 -0
- data/lib/rubocop/cop/rails/render_plain_text.rb +6 -3
- data/lib/rubocop/cop/rails/root_pathname_methods.rb +5 -5
- data/lib/rubocop/cop/rails/skips_model_validations.rb +3 -0
- data/lib/rubocop/cop/rails/validation.rb +4 -1
- data/lib/rubocop/cop/rails/where_equals.rb +22 -11
- data/lib/rubocop/cop/rails/where_not.rb +5 -5
- data/lib/rubocop/cop/rails/where_range.rb +83 -42
- data/lib/rubocop/cop/rails_cops.rb +1 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +1 -1
- data/lib/rubocop/rails/version.rb +1 -1
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c18cefb961b3100a871e2411c7a029244eda6998d793e3ece62aef1adcd4c571
|
4
|
+
data.tar.gz: 24cde448ef91e689e05a2357c1aa0cbcc4d61630f1bb6e7fcaf8999ff7678105
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7be18be7c11552f4a8d460e4833b57881ec3244d3abc7509ef3f9aa78533e3ff77e0ac60cd1737c608c8a724aed4b37fd1faed06ccb689aabf1e0a21970beef6
|
7
|
+
data.tar.gz: fb09716c5cb563cff4eaa33e68f0657424dce2650a9c7da8e37e6876ee8ee12a942ef2b096cb5604ee5a457a7f951dbd2198d290d0f5e0486c04b36e5a2679bc
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# RuboCop Rails
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/rubocop-rails)
|
4
|
-
[](https://github.com/rubocop/rubocop-rails/actions/workflows/test.yml)
|
5
5
|
|
6
6
|
A [RuboCop](https://github.com/rubocop/rubocop) extension focused on enforcing Rails best practices and coding conventions.
|
7
7
|
|
data/config/default.yml
CHANGED
@@ -424,6 +424,14 @@ Rails/EnumHash:
|
|
424
424
|
Include:
|
425
425
|
- app/models/**/*.rb
|
426
426
|
|
427
|
+
Rails/EnumSyntax:
|
428
|
+
Description: 'Use positional arguments over keyword arguments when defining enums.'
|
429
|
+
Enabled: pending
|
430
|
+
Severity: warning
|
431
|
+
VersionAdded: '2.26'
|
432
|
+
Include:
|
433
|
+
- app/models/**/*.rb
|
434
|
+
|
427
435
|
Rails/EnumUniqueness:
|
428
436
|
Description: 'Avoid duplicate integers in hash-syntax `enum` declaration.'
|
429
437
|
Enabled: true
|
@@ -1018,8 +1026,9 @@ Rails/SkipsModelValidations:
|
|
1018
1026
|
See reference for more information.
|
1019
1027
|
Reference: 'https://guides.rubyonrails.org/active_record_validations.html#skipping-validations'
|
1020
1028
|
Enabled: true
|
1029
|
+
Safe: false
|
1021
1030
|
VersionAdded: '0.47'
|
1022
|
-
VersionChanged: '2.
|
1031
|
+
VersionChanged: '2.25'
|
1023
1032
|
ForbiddenMethods:
|
1024
1033
|
- decrement!
|
1025
1034
|
- decrement_counter
|
@@ -1184,12 +1193,12 @@ Rails/Validation:
|
|
1184
1193
|
- app/models/**/*.rb
|
1185
1194
|
|
1186
1195
|
Rails/WhereEquals:
|
1187
|
-
Description: 'Pass conditions to `where` as a hash instead of manually constructing SQL.'
|
1196
|
+
Description: 'Pass conditions to `where` and `where.not` as a hash instead of manually constructing SQL.'
|
1188
1197
|
StyleGuide: 'https://rails.rubystyle.guide/#hash-conditions'
|
1189
1198
|
Enabled: 'pending'
|
1190
1199
|
SafeAutoCorrect: false
|
1191
1200
|
VersionAdded: '2.9'
|
1192
|
-
VersionChanged: '2.
|
1201
|
+
VersionChanged: '2.26'
|
1193
1202
|
|
1194
1203
|
Rails/WhereExists:
|
1195
1204
|
Description: 'Prefer `exists?(...)` over `where(...).exists?`.'
|
@@ -1226,12 +1235,17 @@ Rails/WhereRange:
|
|
1226
1235
|
Description: 'Use ranges in `where` instead of manually constructing SQL.'
|
1227
1236
|
StyleGuide: 'https://rails.rubystyle.guide/#where-ranges'
|
1228
1237
|
Enabled: pending
|
1238
|
+
SafeAutoCorrect: false
|
1229
1239
|
VersionAdded: '2.25'
|
1230
1240
|
|
1231
1241
|
# Accept `redirect_to(...) and return` and similar cases.
|
1232
1242
|
Style/AndOr:
|
1233
1243
|
EnforcedStyle: conditionals
|
1234
1244
|
|
1245
|
+
Style/CollectionCompact:
|
1246
|
+
AllowedReceivers:
|
1247
|
+
- params
|
1248
|
+
|
1235
1249
|
Style/FormatStringToken:
|
1236
1250
|
AllowedMethods:
|
1237
1251
|
- redirect
|
@@ -72,13 +72,13 @@ module RuboCop
|
|
72
72
|
if (node = context.each_ancestor(:if, :rescue).first)
|
73
73
|
return false if use_redirect_to?(context)
|
74
74
|
|
75
|
-
context = node
|
76
|
-
elsif context.right_siblings.empty?
|
77
|
-
return true
|
75
|
+
context = node.rescue_type? ? node.parent : node
|
78
76
|
end
|
79
|
-
context = context.right_siblings
|
80
77
|
|
81
|
-
context.
|
78
|
+
siblings = context.right_siblings
|
79
|
+
return true if siblings.empty?
|
80
|
+
|
81
|
+
siblings.compact.any? do |render_candidate|
|
82
82
|
render?(render_candidate)
|
83
83
|
end
|
84
84
|
end
|
@@ -113,8 +113,10 @@ module RuboCop
|
|
113
113
|
MYSQL_COMBINABLE_ALTER_METHODS = %i[rename_column add_index remove_index].freeze
|
114
114
|
|
115
115
|
POSTGRESQL_COMBINABLE_TRANSFORMATIONS = %i[change_default].freeze
|
116
|
+
POSTGRESQL_COMBINABLE_TRANSFORMATIONS_SINCE_6_1 = %i[change_null].freeze
|
116
117
|
|
117
118
|
POSTGRESQL_COMBINABLE_ALTER_METHODS = %i[change_column_default].freeze
|
119
|
+
POSTGRESQL_COMBINABLE_ALTER_METHODS_SINCE_6_1 = %i[change_column_null].freeze
|
118
120
|
|
119
121
|
def on_def(node)
|
120
122
|
return unless support_bulk_alter?
|
@@ -138,9 +140,9 @@ module RuboCop
|
|
138
140
|
return unless support_bulk_alter?
|
139
141
|
return unless node.command?(:change_table)
|
140
142
|
return if include_bulk_options?(node)
|
141
|
-
return unless node.block_node
|
143
|
+
return unless (body = node.block_node&.body)
|
142
144
|
|
143
|
-
send_nodes = send_nodes_from_change_table_block(
|
145
|
+
send_nodes = send_nodes_from_change_table_block(body)
|
144
146
|
|
145
147
|
add_offense_for_change_table(node) if count_transformations(send_nodes) > 1
|
146
148
|
end
|
@@ -196,7 +198,9 @@ module RuboCop
|
|
196
198
|
when MYSQL
|
197
199
|
COMBINABLE_ALTER_METHODS + MYSQL_COMBINABLE_ALTER_METHODS
|
198
200
|
when POSTGRESQL
|
199
|
-
COMBINABLE_ALTER_METHODS + POSTGRESQL_COMBINABLE_ALTER_METHODS
|
201
|
+
result = COMBINABLE_ALTER_METHODS + POSTGRESQL_COMBINABLE_ALTER_METHODS
|
202
|
+
result += POSTGRESQL_COMBINABLE_ALTER_METHODS_SINCE_6_1 if target_rails_version >= 6.1
|
203
|
+
result
|
200
204
|
end
|
201
205
|
end
|
202
206
|
|
@@ -205,7 +209,9 @@ module RuboCop
|
|
205
209
|
when MYSQL
|
206
210
|
COMBINABLE_TRANSFORMATIONS + MYSQL_COMBINABLE_TRANSFORMATIONS
|
207
211
|
when POSTGRESQL
|
208
|
-
COMBINABLE_TRANSFORMATIONS + POSTGRESQL_COMBINABLE_TRANSFORMATIONS
|
212
|
+
result = COMBINABLE_TRANSFORMATIONS + POSTGRESQL_COMBINABLE_TRANSFORMATIONS
|
213
|
+
result += POSTGRESQL_COMBINABLE_TRANSFORMATIONS_SINCE_6_1 if target_rails_version >= 6.1
|
214
|
+
result
|
209
215
|
end
|
210
216
|
end
|
211
217
|
|
@@ -16,7 +16,6 @@ module RuboCop
|
|
16
16
|
# And `compact_blank!` has different implementations for `Array`, `Hash`, and
|
17
17
|
# `ActionController::Parameters`.
|
18
18
|
# `Array#compact_blank!`, `Hash#compact_blank!` are equivalent to `delete_if(&:blank?)`.
|
19
|
-
# `ActionController::Parameters#compact_blank!` is equivalent to `reject!(&:blank?)`.
|
20
19
|
# If the cop makes a mistake, autocorrected code may get unexpected behavior.
|
21
20
|
#
|
22
21
|
# @example
|
@@ -24,6 +23,8 @@ module RuboCop
|
|
24
23
|
# # bad
|
25
24
|
# collection.reject(&:blank?)
|
26
25
|
# collection.reject { |_k, v| v.blank? }
|
26
|
+
# collection.select(&:present?)
|
27
|
+
# collection.select { |_k, v| v.present? }
|
27
28
|
#
|
28
29
|
# # good
|
29
30
|
# collection.compact_blank
|
@@ -31,8 +32,8 @@ module RuboCop
|
|
31
32
|
# # bad
|
32
33
|
# collection.delete_if(&:blank?) # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
|
33
34
|
# collection.delete_if { |_k, v| v.blank? } # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
|
34
|
-
# collection.
|
35
|
-
# collection.
|
35
|
+
# collection.keep_if(&:present?) # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
|
36
|
+
# collection.keep_if { |_k, v| v.present? } # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
|
36
37
|
#
|
37
38
|
# # good
|
38
39
|
# collection.compact_blank!
|
@@ -43,24 +44,38 @@ module RuboCop
|
|
43
44
|
extend TargetRailsVersion
|
44
45
|
|
45
46
|
MSG = 'Use `%<preferred_method>s` instead.'
|
46
|
-
RESTRICT_ON_SEND = %i[reject delete_if
|
47
|
+
RESTRICT_ON_SEND = %i[reject delete_if select keep_if].freeze
|
47
48
|
|
48
49
|
minimum_target_rails_version 6.1
|
49
50
|
|
50
51
|
def_node_matcher :reject_with_block?, <<~PATTERN
|
51
52
|
(block
|
52
|
-
(send _ {:reject :delete_if
|
53
|
+
(send _ {:reject :delete_if})
|
53
54
|
$(args ...)
|
54
55
|
(send
|
55
56
|
$(lvar _) :blank?))
|
56
57
|
PATTERN
|
57
58
|
|
58
59
|
def_node_matcher :reject_with_block_pass?, <<~PATTERN
|
59
|
-
(send _ {:reject :delete_if
|
60
|
+
(send _ {:reject :delete_if}
|
60
61
|
(block_pass
|
61
62
|
(sym :blank?)))
|
62
63
|
PATTERN
|
63
64
|
|
65
|
+
def_node_matcher :select_with_block?, <<~PATTERN
|
66
|
+
(block
|
67
|
+
(send _ {:select :keep_if})
|
68
|
+
$(args ...)
|
69
|
+
(send
|
70
|
+
$(lvar _) :present?))
|
71
|
+
PATTERN
|
72
|
+
|
73
|
+
def_node_matcher :select_with_block_pass?, <<~PATTERN
|
74
|
+
(send _ {:select :keep_if}
|
75
|
+
(block-pass
|
76
|
+
(sym :present?)))
|
77
|
+
PATTERN
|
78
|
+
|
64
79
|
def on_send(node)
|
65
80
|
return unless bad_method?(node)
|
66
81
|
|
@@ -75,8 +90,10 @@ module RuboCop
|
|
75
90
|
|
76
91
|
def bad_method?(node)
|
77
92
|
return true if reject_with_block_pass?(node)
|
93
|
+
return true if select_with_block_pass?(node)
|
78
94
|
|
79
|
-
|
95
|
+
arguments, receiver_in_block = reject_with_block?(node.parent) || select_with_block?(node.parent)
|
96
|
+
if arguments
|
80
97
|
return use_single_value_block_argument?(arguments, receiver_in_block) ||
|
81
98
|
use_hash_value_block_argument?(arguments, receiver_in_block)
|
82
99
|
end
|
@@ -103,7 +120,7 @@ module RuboCop
|
|
103
120
|
end
|
104
121
|
|
105
122
|
def preferred_method(node)
|
106
|
-
node.method?(:reject) ? 'compact_blank' : 'compact_blank!'
|
123
|
+
node.method?(:reject) || node.method?(:select) ? 'compact_blank' : 'compact_blank!'
|
107
124
|
end
|
108
125
|
end
|
109
126
|
end
|
@@ -12,10 +12,10 @@ module RuboCop
|
|
12
12
|
# The cop also reports warnings when you are using `to_time` method,
|
13
13
|
# because it doesn't know about Rails time zone either.
|
14
14
|
#
|
15
|
-
# Two styles are supported for this cop. When `EnforcedStyle` is
|
15
|
+
# Two styles are supported for this cop. When `EnforcedStyle` is `strict`
|
16
16
|
# then the Date methods `today`, `current`, `yesterday`, and `tomorrow`
|
17
17
|
# are prohibited and the usage of both `to_time`
|
18
|
-
# and
|
18
|
+
# and `to_time_in_current_zone` are reported as warning.
|
19
19
|
#
|
20
20
|
# When `EnforcedStyle` is `flexible` then only `Date.today` is prohibited.
|
21
21
|
#
|
@@ -12,6 +12,12 @@ module RuboCop
|
|
12
12
|
#
|
13
13
|
# @example
|
14
14
|
# # bad
|
15
|
+
# enum :status, [:active, :archived]
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# enum :status, { active: 0, archived: 1 }
|
19
|
+
#
|
20
|
+
# # bad
|
15
21
|
# enum status: [:active, :archived]
|
16
22
|
#
|
17
23
|
# # good
|
@@ -23,7 +29,11 @@ module RuboCop
|
|
23
29
|
MSG = 'Enum defined as an array found in `%<enum>s` enum declaration. Use hash syntax instead.'
|
24
30
|
RESTRICT_ON_SEND = %i[enum].freeze
|
25
31
|
|
26
|
-
def_node_matcher :
|
32
|
+
def_node_matcher :enum_with_array?, <<~PATTERN
|
33
|
+
(send nil? :enum $_ ${array} ...)
|
34
|
+
PATTERN
|
35
|
+
|
36
|
+
def_node_matcher :enum_with_old_syntax?, <<~PATTERN
|
27
37
|
(send nil? :enum (hash $...))
|
28
38
|
PATTERN
|
29
39
|
|
@@ -32,17 +42,19 @@ module RuboCop
|
|
32
42
|
PATTERN
|
33
43
|
|
34
44
|
def on_send(node)
|
35
|
-
|
45
|
+
target_rails_version >= 7.0 && enum_with_array?(node) do |key, array|
|
46
|
+
add_offense(array, message: message(key)) do |corrector|
|
47
|
+
corrector.replace(array, build_hash(array))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
enum_with_old_syntax?(node) do |pairs|
|
36
52
|
pairs.each do |pair|
|
37
53
|
key, array = array_pair?(pair)
|
38
54
|
next unless key
|
39
55
|
|
40
|
-
add_offense(array, message:
|
41
|
-
|
42
|
-
"#{source(elem)} => #{index}"
|
43
|
-
end.join(', ')
|
44
|
-
|
45
|
-
corrector.replace(array, "{#{hash}}")
|
56
|
+
add_offense(array, message: message(key)) do |corrector|
|
57
|
+
corrector.replace(array, build_hash(array))
|
46
58
|
end
|
47
59
|
end
|
48
60
|
end
|
@@ -50,6 +62,10 @@ module RuboCop
|
|
50
62
|
|
51
63
|
private
|
52
64
|
|
65
|
+
def message(key)
|
66
|
+
format(MSG, enum: enum_name(key))
|
67
|
+
end
|
68
|
+
|
53
69
|
def enum_name(key)
|
54
70
|
case key.type
|
55
71
|
when :sym, :str
|
@@ -69,6 +85,13 @@ module RuboCop
|
|
69
85
|
elem.source
|
70
86
|
end
|
71
87
|
end
|
88
|
+
|
89
|
+
def build_hash(array)
|
90
|
+
hash = array.children.each_with_index.map do |elem, index|
|
91
|
+
"#{source(elem)} => #{index}"
|
92
|
+
end.join(', ')
|
93
|
+
"{#{hash}}"
|
94
|
+
end
|
72
95
|
end
|
73
96
|
end
|
74
97
|
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Looks for enums written with keyword arguments syntax.
|
7
|
+
#
|
8
|
+
# Defining enums with keyword arguments syntax is deprecated and will be removed in Rails 8.0.
|
9
|
+
# Positional arguments should be used instead:
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# # bad
|
13
|
+
# enum status: { active: 0, archived: 1 }, _prefix: true
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# enum :status, { active: 0, archived: 1 }, prefix: true
|
17
|
+
#
|
18
|
+
class EnumSyntax < Base
|
19
|
+
extend AutoCorrector
|
20
|
+
extend TargetRailsVersion
|
21
|
+
|
22
|
+
minimum_target_rails_version 7.0
|
23
|
+
|
24
|
+
MSG = 'Enum defined with keyword arguments in `%<enum>s` enum declaration. Use positional arguments instead.'
|
25
|
+
MSG_OPTIONS = 'Enum defined with deprecated options in `%<enum>s` enum declaration. Remove the `_` prefix.'
|
26
|
+
RESTRICT_ON_SEND = %i[enum].freeze
|
27
|
+
OPTION_NAMES = %w[prefix suffix scopes default].freeze
|
28
|
+
|
29
|
+
def_node_matcher :enum?, <<~PATTERN
|
30
|
+
(send nil? :enum (hash $...))
|
31
|
+
PATTERN
|
32
|
+
|
33
|
+
def_node_matcher :enum_with_options?, <<~PATTERN
|
34
|
+
(send nil? :enum $_ ${array hash} $_)
|
35
|
+
PATTERN
|
36
|
+
|
37
|
+
def_node_matcher :enum_values, <<~PATTERN
|
38
|
+
(pair $_ ${array hash})
|
39
|
+
PATTERN
|
40
|
+
|
41
|
+
def_node_matcher :enum_options, <<~PATTERN
|
42
|
+
(pair $_ $_)
|
43
|
+
PATTERN
|
44
|
+
|
45
|
+
def on_send(node)
|
46
|
+
check_and_correct_keyword_args(node)
|
47
|
+
check_enum_options(node)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def check_and_correct_keyword_args(node)
|
53
|
+
enum?(node) do |pairs|
|
54
|
+
pairs.each do |pair|
|
55
|
+
key, values = enum_values(pair)
|
56
|
+
next unless key
|
57
|
+
|
58
|
+
correct_keyword_args(node, key, values, pairs[1..])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def check_enum_options(node)
|
64
|
+
enum_with_options?(node) do |key, _, options|
|
65
|
+
options.children.each do |option|
|
66
|
+
name, = enum_options(option)
|
67
|
+
|
68
|
+
add_offense(name, message: format(MSG_OPTIONS, enum: enum_name_value(key))) if name.source[0] == '_'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def correct_keyword_args(node, key, values, options)
|
74
|
+
add_offense(values, message: format(MSG, enum: enum_name_value(key))) do |corrector|
|
75
|
+
# TODO: Multi-line autocorrect could be implemented in the future.
|
76
|
+
next if multiple_enum_definitions?(node)
|
77
|
+
|
78
|
+
preferred_syntax = "enum #{enum_name(key)}, #{values.source}#{correct_options(options)}"
|
79
|
+
|
80
|
+
corrector.replace(node, preferred_syntax)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def multiple_enum_definitions?(node)
|
85
|
+
keys = node.first_argument.keys.map { |key| key.source.delete_prefix('_') }
|
86
|
+
filterred_keys = keys.filter { |key| !OPTION_NAMES.include?(key) }
|
87
|
+
filterred_keys.size >= 2
|
88
|
+
end
|
89
|
+
|
90
|
+
def enum_name_value(key)
|
91
|
+
case key.type
|
92
|
+
when :sym, :str
|
93
|
+
key.value
|
94
|
+
else
|
95
|
+
key.source
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def enum_name(elem)
|
100
|
+
case elem.type
|
101
|
+
when :str
|
102
|
+
elem.value.dump
|
103
|
+
when :sym
|
104
|
+
elem.value.inspect
|
105
|
+
else
|
106
|
+
elem.source
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def correct_options(options)
|
111
|
+
corrected_options = options.map do |pair|
|
112
|
+
name = if pair.key.source[0] == '_'
|
113
|
+
pair.key.source[1..]
|
114
|
+
else
|
115
|
+
pair.key.source
|
116
|
+
end
|
117
|
+
|
118
|
+
"#{name}: #{pair.value.source}"
|
119
|
+
end.join(', ')
|
120
|
+
|
121
|
+
", #{corrected_options}" unless corrected_options.empty?
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -7,6 +7,18 @@ module RuboCop
|
|
7
7
|
#
|
8
8
|
# @example
|
9
9
|
# # bad
|
10
|
+
# enum :status, { active: 0, archived: 0 }
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# enum :status, { active: 0, archived: 1 }
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# enum :status, [:active, :archived, :active]
|
17
|
+
#
|
18
|
+
# # good
|
19
|
+
# enum :status, [:active, :archived]
|
20
|
+
#
|
21
|
+
# # bad
|
10
22
|
# enum status: { active: 0, archived: 0 }
|
11
23
|
#
|
12
24
|
# # good
|
@@ -24,6 +36,10 @@ module RuboCop
|
|
24
36
|
RESTRICT_ON_SEND = %i[enum].freeze
|
25
37
|
|
26
38
|
def_node_matcher :enum?, <<~PATTERN
|
39
|
+
(send nil? :enum $_ ${array hash} ...)
|
40
|
+
PATTERN
|
41
|
+
|
42
|
+
def_node_matcher :enum_with_old_syntax?, <<~PATTERN
|
27
43
|
(send nil? :enum (hash $...))
|
28
44
|
PATTERN
|
29
45
|
|
@@ -32,15 +48,17 @@ module RuboCop
|
|
32
48
|
PATTERN
|
33
49
|
|
34
50
|
def on_send(node)
|
35
|
-
enum?(node) do |
|
51
|
+
enum?(node) do |key, args|
|
52
|
+
consecutive_duplicates(args.values).each do |item|
|
53
|
+
add_offense(item, message: message(key, item))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
enum_with_old_syntax?(node) do |pairs|
|
36
58
|
pairs.each do |pair|
|
37
59
|
enum_values(pair) do |key, args|
|
38
|
-
|
39
|
-
|
40
|
-
next unless duplicates?(items)
|
41
|
-
|
42
|
-
consecutive_duplicates(items).each do |item|
|
43
|
-
add_offense(item, message: format(MSG, value: item.source, enum: enum_name(key)))
|
60
|
+
consecutive_duplicates(args.values).each do |item|
|
61
|
+
add_offense(item, message: message(key, item))
|
44
62
|
end
|
45
63
|
end
|
46
64
|
end
|
@@ -57,6 +75,10 @@ module RuboCop
|
|
57
75
|
key.source
|
58
76
|
end
|
59
77
|
end
|
78
|
+
|
79
|
+
def message(key, item)
|
80
|
+
format(MSG, value: item.source, enum: enum_name(key))
|
81
|
+
end
|
60
82
|
end
|
61
83
|
end
|
62
84
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
# Checks for calls to `link_to` that contain a
|
6
|
+
# Checks for calls to `link_to`, `link_to_if`, and `link_to_unless` methods that contain a
|
7
7
|
# `target: '_blank'` but no `rel: 'noopener'`. This can be a security
|
8
8
|
# risk as the loaded page will have control over the previous page
|
9
9
|
# and could change its location for phishing purposes.
|
@@ -24,7 +24,7 @@ module RuboCop
|
|
24
24
|
extend AutoCorrector
|
25
25
|
|
26
26
|
MSG = 'Specify a `:rel` option containing noopener.'
|
27
|
-
RESTRICT_ON_SEND = %i[link_to].freeze
|
27
|
+
RESTRICT_ON_SEND = %i[link_to link_to_if link_to_unless].freeze
|
28
28
|
|
29
29
|
def_node_matcher :blank_target?, <<~PATTERN
|
30
30
|
(pair {(sym :target) (str "target")} {(str "_blank") (sym :_blank)})
|
@@ -136,6 +136,8 @@ module RuboCop
|
|
136
136
|
|
137
137
|
def check_change_table(node)
|
138
138
|
change_table?(node) do |table|
|
139
|
+
next unless node.body
|
140
|
+
|
139
141
|
children = node.body.begin_type? ? node.body.children : [node.body]
|
140
142
|
children.each do |child|
|
141
143
|
check_add_column_in_change_table(child, table)
|
@@ -7,17 +7,26 @@ module RuboCop
|
|
7
7
|
# and can be replaced with `select`.
|
8
8
|
#
|
9
9
|
# Since `pluck` is an eager method and hits the database immediately,
|
10
|
-
# using `select` helps to avoid additional database queries
|
10
|
+
# using `select` helps to avoid additional database queries by running as
|
11
|
+
# a subquery.
|
11
12
|
#
|
12
|
-
# This cop has two
|
13
|
-
#
|
14
|
-
# (
|
13
|
+
# This cop has two modes of enforcement. When the `EnforcedStyle` is set
|
14
|
+
# to `conservative` (the default), only calls to `pluck` on a constant
|
15
|
+
# (e.g. a model class) within `where` are considered offenses.
|
15
16
|
#
|
16
17
|
# @safety
|
17
|
-
# When
|
18
|
-
# `where`
|
19
|
-
#
|
20
|
-
# `ActiveRecord::Relation` instance
|
18
|
+
# When `EnforcedStyle` is set to `aggressive`, all calls to `pluck`
|
19
|
+
# within `where` are considered offenses. This might lead to false
|
20
|
+
# positives because the check cannot distinguish between calls to
|
21
|
+
# `pluck` on an `ActiveRecord::Relation` instance and calls to `pluck`
|
22
|
+
# on an `Array` instance.
|
23
|
+
#
|
24
|
+
# Additionally, when using a subquery with the SQL `IN` operator,
|
25
|
+
# databases like PostgreSQL and MySQL can't optimize complex queries as
|
26
|
+
# well. They need to scan all records of the outer table against the
|
27
|
+
# subquery result sequentially, rather than using an index. This can
|
28
|
+
# cause significant performance issues compared to writing the query
|
29
|
+
# differently or using `pluck`.
|
21
30
|
#
|
22
31
|
# @example
|
23
32
|
# # bad
|
@@ -10,25 +10,39 @@ module RuboCop
|
|
10
10
|
# # bad
|
11
11
|
# 3.day.ago
|
12
12
|
# 1.months.ago
|
13
|
+
# 5.megabyte
|
14
|
+
# 1.gigabyte
|
13
15
|
#
|
14
16
|
# # good
|
15
17
|
# 3.days.ago
|
16
18
|
# 1.month.ago
|
19
|
+
# 5.megabytes
|
20
|
+
# 1.gigabyte
|
17
21
|
class PluralizationGrammar < Base
|
18
22
|
extend AutoCorrector
|
19
23
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
24
|
+
SINGULAR_METHODS = {
|
25
|
+
second: :seconds,
|
26
|
+
minute: :minutes,
|
27
|
+
hour: :hours,
|
28
|
+
day: :days,
|
29
|
+
week: :weeks,
|
30
|
+
fortnight: :fortnights,
|
31
|
+
month: :months,
|
32
|
+
year: :years,
|
33
|
+
byte: :bytes,
|
34
|
+
kilobyte: :kilobytes,
|
35
|
+
megabyte: :megabytes,
|
36
|
+
gigabyte: :gigabytes,
|
37
|
+
terabyte: :terabytes,
|
38
|
+
petabyte: :petabytes,
|
39
|
+
exabyte: :exabytes,
|
40
|
+
zettabyte: :zettabytes
|
41
|
+
}.freeze
|
42
|
+
|
43
|
+
RESTRICT_ON_SEND = SINGULAR_METHODS.keys + SINGULAR_METHODS.values
|
44
|
+
|
45
|
+
PLURAL_METHODS = SINGULAR_METHODS.invert.freeze
|
32
46
|
|
33
47
|
MSG = 'Prefer `%<number>s.%<correct>s`.'
|
34
48
|
|
@@ -86,15 +100,15 @@ module RuboCop
|
|
86
100
|
end
|
87
101
|
|
88
102
|
def pluralize(method_name)
|
89
|
-
|
103
|
+
SINGULAR_METHODS.fetch(method_name.to_sym).to_s
|
90
104
|
end
|
91
105
|
|
92
106
|
def singularize(method_name)
|
93
|
-
|
107
|
+
PLURAL_METHODS.fetch(method_name.to_sym).to_s
|
94
108
|
end
|
95
109
|
|
96
110
|
def duration_method?(method_name)
|
97
|
-
|
111
|
+
SINGULAR_METHODS.key?(method_name) || PLURAL_METHODS.key?(method_name)
|
98
112
|
end
|
99
113
|
end
|
100
114
|
end
|
@@ -39,6 +39,9 @@ module RuboCop
|
|
39
39
|
MSG = 'Remove explicit presence validation for %<association>s.'
|
40
40
|
RESTRICT_ON_SEND = %i[validates].freeze
|
41
41
|
|
42
|
+
# From https://github.com/rails/rails/blob/7a0bf93b9dd291c7f61121a41b3a813ac8857e6a/activemodel/lib/active_model/validations/validates.rb#L157-L159
|
43
|
+
NON_VALIDATION_OPTIONS = %i[if unless on allow_blank allow_nil strict].freeze
|
44
|
+
|
42
45
|
minimum_target_rails_version 5.0
|
43
46
|
|
44
47
|
# @!method presence_validation?(node)
|
@@ -170,6 +173,12 @@ module RuboCop
|
|
170
173
|
|
171
174
|
def on_send(node)
|
172
175
|
presence_validation?(node) do |all_keys, options, presence|
|
176
|
+
# If presence is the only validation option and other non-validation options
|
177
|
+
# are present, removing it will cause rails to error.
|
178
|
+
used_option_keys = options.keys.select(&:sym_type?).map(&:value)
|
179
|
+
remaining_validations = used_option_keys - NON_VALIDATION_OPTIONS - [:presence]
|
180
|
+
return if remaining_validations.none? && options.keys.length > 1
|
181
|
+
|
173
182
|
keys = non_optional_belongs_to(node.parent, all_keys)
|
174
183
|
return if keys.none?
|
175
184
|
|
@@ -53,9 +53,12 @@ module RuboCop
|
|
53
53
|
node.pairs.find { |p| p.key.value.to_sym == :content_type }
|
54
54
|
end
|
55
55
|
|
56
|
-
def compatible_content_type?(
|
57
|
-
|
58
|
-
|
56
|
+
def compatible_content_type?(pair_node)
|
57
|
+
if pair_node.nil?
|
58
|
+
!cop_config['ContentTypeCompatibility']
|
59
|
+
elsif pair_node.value.respond_to?(:value)
|
60
|
+
pair_node.value.value == 'text/plain'
|
61
|
+
end
|
59
62
|
end
|
60
63
|
|
61
64
|
def replacement(rest_options, option_value)
|
@@ -38,7 +38,7 @@ module RuboCop
|
|
38
38
|
|
39
39
|
MSG = '`%<rails_root>s` is a `Pathname` so you can just append `#%<method>s`.'
|
40
40
|
|
41
|
-
DIR_GLOB_METHODS = %i[glob].to_set.freeze
|
41
|
+
DIR_GLOB_METHODS = %i[[] glob].to_set.freeze
|
42
42
|
|
43
43
|
DIR_NON_GLOB_METHODS = %i[
|
44
44
|
children
|
@@ -171,7 +171,7 @@ module RuboCop
|
|
171
171
|
|
172
172
|
def_node_matcher :dir_glob?, <<~PATTERN
|
173
173
|
(send
|
174
|
-
(const {cbase nil?} :Dir)
|
174
|
+
(const {cbase nil?} :Dir) DIR_GLOB_METHODS ...)
|
175
175
|
PATTERN
|
176
176
|
|
177
177
|
def_node_matcher :rails_root_pathname?, <<~PATTERN
|
@@ -190,7 +190,7 @@ module RuboCop
|
|
190
190
|
evidence(node) do |method, path, args, rails_root|
|
191
191
|
add_offense(node, message: format(MSG, method: method, rails_root: rails_root.source)) do |corrector|
|
192
192
|
replacement = if dir_glob?(node)
|
193
|
-
build_path_glob_replacement(path
|
193
|
+
build_path_glob_replacement(path)
|
194
194
|
else
|
195
195
|
build_path_replacement(path, method, args)
|
196
196
|
end
|
@@ -217,12 +217,12 @@ module RuboCop
|
|
217
217
|
end
|
218
218
|
end
|
219
219
|
|
220
|
-
def build_path_glob_replacement(path
|
220
|
+
def build_path_glob_replacement(path)
|
221
221
|
receiver = range_between(path.source_range.begin_pos, path.children.first.loc.selector.end_pos).source
|
222
222
|
|
223
223
|
argument = path.arguments.one? ? path.first_argument.source : join_arguments(path.arguments)
|
224
224
|
|
225
|
-
"#{receiver}
|
225
|
+
"#{receiver}.glob(#{argument})"
|
226
226
|
end
|
227
227
|
|
228
228
|
def build_path_replacement(path, method, args)
|
@@ -9,6 +9,9 @@ module RuboCop
|
|
9
9
|
#
|
10
10
|
# Methods may be ignored from this rule by configuring a `AllowedMethods`.
|
11
11
|
#
|
12
|
+
# @safety
|
13
|
+
# This cop is unsafe if the receiver object is not an Active Record object.
|
14
|
+
#
|
12
15
|
# @example
|
13
16
|
# # bad
|
14
17
|
# Article.first.decrement!(:view_count)
|
@@ -8,6 +8,7 @@ module RuboCop
|
|
8
8
|
# @example
|
9
9
|
# # bad
|
10
10
|
# validates_acceptance_of :foo
|
11
|
+
# validates_comparison_of :foo
|
11
12
|
# validates_confirmation_of :foo
|
12
13
|
# validates_exclusion_of :foo
|
13
14
|
# validates_format_of :foo
|
@@ -22,6 +23,7 @@ module RuboCop
|
|
22
23
|
# # good
|
23
24
|
# validates :foo, acceptance: true
|
24
25
|
# validates :foo, confirmation: true
|
26
|
+
# validates :foo, comparison: true
|
25
27
|
# validates :foo, exclusion: true
|
26
28
|
# validates :foo, format: true
|
27
29
|
# validates :foo, inclusion: true
|
@@ -39,6 +41,7 @@ module RuboCop
|
|
39
41
|
|
40
42
|
TYPES = %w[
|
41
43
|
acceptance
|
44
|
+
comparison
|
42
45
|
confirmation
|
43
46
|
exclusion
|
44
47
|
format
|
@@ -56,11 +59,11 @@ module RuboCop
|
|
56
59
|
|
57
60
|
def on_send(node)
|
58
61
|
return if node.receiver
|
62
|
+
return unless (last_argument = node.last_argument)
|
59
63
|
|
60
64
|
range = node.loc.selector
|
61
65
|
|
62
66
|
add_offense(range, message: message(node)) do |corrector|
|
63
|
-
last_argument = node.last_argument
|
64
67
|
return if !last_argument.literal? && !last_argument.splat_type? && !frozen_array_argument?(last_argument)
|
65
68
|
|
66
69
|
corrector.replace(range, 'validates')
|
@@ -4,7 +4,8 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
6
|
# Identifies places where manually constructed SQL
|
7
|
-
# in `where` can be replaced with
|
7
|
+
# in `where` and `where.not` can be replaced with
|
8
|
+
# `where(attribute: value)` and `where.not(attribute: value)`.
|
8
9
|
#
|
9
10
|
# @safety
|
10
11
|
# This cop's autocorrection is unsafe because is may change SQL.
|
@@ -13,6 +14,7 @@ module RuboCop
|
|
13
14
|
# @example
|
14
15
|
# # bad
|
15
16
|
# User.where('name = ?', 'Gabe')
|
17
|
+
# User.where.not('name = ?', 'Gabe')
|
16
18
|
# User.where('name = :name', name: 'Gabe')
|
17
19
|
# User.where('name IS NULL')
|
18
20
|
# User.where('name IN (?)', ['john', 'jane'])
|
@@ -21,6 +23,7 @@ module RuboCop
|
|
21
23
|
#
|
22
24
|
# # good
|
23
25
|
# User.where(name: 'Gabe')
|
26
|
+
# User.where.not(name: 'Gabe')
|
24
27
|
# User.where(name: nil)
|
25
28
|
# User.where(name: ['john', 'jane'])
|
26
29
|
# User.where(users: { name: 'Gabe' })
|
@@ -29,25 +32,27 @@ module RuboCop
|
|
29
32
|
extend AutoCorrector
|
30
33
|
|
31
34
|
MSG = 'Use `%<good_method>s` instead of manually constructing SQL.'
|
32
|
-
RESTRICT_ON_SEND = %i[where].freeze
|
35
|
+
RESTRICT_ON_SEND = %i[where not].freeze
|
33
36
|
|
34
37
|
def_node_matcher :where_method_call?, <<~PATTERN
|
35
38
|
{
|
36
|
-
(call _ :where (array $str_type? $_ ?))
|
37
|
-
(call _ :where $str_type? $_ ?)
|
39
|
+
(call _ {:where :not} (array $str_type? $_ ?))
|
40
|
+
(call _ {:where :not} $str_type? $_ ?)
|
38
41
|
}
|
39
42
|
PATTERN
|
40
43
|
|
41
44
|
def on_send(node)
|
45
|
+
return if node.method?(:not) && !where_not?(node)
|
46
|
+
|
42
47
|
where_method_call?(node) do |template_node, value_node|
|
43
48
|
value_node = value_node.first
|
44
49
|
|
45
50
|
range = offense_range(node)
|
46
51
|
|
47
|
-
|
48
|
-
return unless
|
52
|
+
column, value = extract_column_and_value(template_node, value_node)
|
53
|
+
return unless value
|
49
54
|
|
50
|
-
good_method = build_good_method(
|
55
|
+
good_method = build_good_method(node.method_name, column, value)
|
51
56
|
message = format(MSG, good_method: good_method)
|
52
57
|
|
53
58
|
add_offense(range, message: message) do |corrector|
|
@@ -73,7 +78,7 @@ module RuboCop
|
|
73
78
|
value =
|
74
79
|
case template_node.value
|
75
80
|
when EQ_ANONYMOUS_RE, IN_ANONYMOUS_RE
|
76
|
-
value_node
|
81
|
+
value_node&.source
|
77
82
|
when EQ_NAMED_RE, IN_NAMED_RE
|
78
83
|
return unless value_node&.hash_type?
|
79
84
|
|
@@ -88,15 +93,21 @@ module RuboCop
|
|
88
93
|
[Regexp.last_match(1), value]
|
89
94
|
end
|
90
95
|
|
91
|
-
def build_good_method(column, value)
|
96
|
+
def build_good_method(method_name, column, value)
|
92
97
|
if column.include?('.')
|
93
98
|
table, column = column.split('.')
|
94
99
|
|
95
|
-
"
|
100
|
+
"#{method_name}(#{table}: { #{column}: #{value} })"
|
96
101
|
else
|
97
|
-
"
|
102
|
+
"#{method_name}(#{column}: #{value})"
|
98
103
|
end
|
99
104
|
end
|
105
|
+
|
106
|
+
def where_not?(node)
|
107
|
+
return false unless (receiver = node.receiver)
|
108
|
+
|
109
|
+
receiver.send_type? && receiver.method?(:where)
|
110
|
+
end
|
100
111
|
end
|
101
112
|
end
|
102
113
|
end
|
@@ -43,10 +43,10 @@ module RuboCop
|
|
43
43
|
|
44
44
|
range = offense_range(node)
|
45
45
|
|
46
|
-
|
47
|
-
return unless
|
46
|
+
column, value = extract_column_and_value(template_node, value_node)
|
47
|
+
return unless value
|
48
48
|
|
49
|
-
good_method = build_good_method(node.loc.dot&.source,
|
49
|
+
good_method = build_good_method(node.loc.dot&.source, column, value)
|
50
50
|
message = format(MSG, good_method: good_method)
|
51
51
|
|
52
52
|
add_offense(range, message: message) do |corrector|
|
@@ -72,9 +72,9 @@ module RuboCop
|
|
72
72
|
value =
|
73
73
|
case template_node.value
|
74
74
|
when NOT_EQ_ANONYMOUS_RE, NOT_IN_ANONYMOUS_RE
|
75
|
-
value_node
|
75
|
+
value_node&.source
|
76
76
|
when NOT_EQ_NAMED_RE, NOT_IN_NAMED_RE
|
77
|
-
return unless value_node
|
77
|
+
return unless value_node&.hash_type?
|
78
78
|
|
79
79
|
pair = value_node.pairs.find { |p| p.key.value.to_sym == Regexp.last_match(2).to_sym }
|
80
80
|
pair.value.source
|
@@ -6,6 +6,14 @@ module RuboCop
|
|
6
6
|
# Identifies places where manually constructed SQL
|
7
7
|
# in `where` can be replaced with ranges.
|
8
8
|
#
|
9
|
+
# @safety
|
10
|
+
# This cop's autocorrection is unsafe because it can change the query
|
11
|
+
# by explicitly attaching the column to the wrong table.
|
12
|
+
# For example, `Booking.joins(:events).where('end_at < ?', Time.current)` will correctly
|
13
|
+
# implicitly attach the `end_at` column to the `events` table. But when autocorrected to
|
14
|
+
# `Booking.joins(:events).where(end_at: ...Time.current)`, it will now be incorrectly
|
15
|
+
# explicitly attached to the `bookings` table.
|
16
|
+
#
|
9
17
|
# @example
|
10
18
|
# # bad
|
11
19
|
# User.where('age >= ?', 18)
|
@@ -37,17 +45,17 @@ module RuboCop
|
|
37
45
|
RESTRICT_ON_SEND = %i[where not].freeze
|
38
46
|
|
39
47
|
# column >= ?
|
40
|
-
GTEQ_ANONYMOUS_RE = /\A([\w.]+)\s+>=\s+\?\z/.freeze
|
48
|
+
GTEQ_ANONYMOUS_RE = /\A\s*([\w.]+)\s+>=\s+\?\s*\z/.freeze
|
41
49
|
# column <[=] ?
|
42
|
-
LTEQ_ANONYMOUS_RE = /\A([\w.]+)\s+(<=?)\s+\?\z/.freeze
|
50
|
+
LTEQ_ANONYMOUS_RE = /\A\s*([\w.]+)\s+(<=?)\s+\?\s*\z/.freeze
|
43
51
|
# column >= ? AND column <[=] ?
|
44
|
-
RANGE_ANONYMOUS_RE = /\A([\w.]+)\s+>=\s+\?\s+AND\s+\1\s+(<=?)\s+\?\z/i.freeze
|
52
|
+
RANGE_ANONYMOUS_RE = /\A\s*([\w.]+)\s+>=\s+\?\s+AND\s+\1\s+(<=?)\s+\?\s*\z/i.freeze
|
45
53
|
# column >= :value
|
46
|
-
GTEQ_NAMED_RE = /\A([\w.]+)\s+>=\s+:(\w+)\z/.freeze
|
54
|
+
GTEQ_NAMED_RE = /\A\s*([\w.]+)\s+>=\s+:(\w+)\s*\z/.freeze
|
47
55
|
# column <[=] :value
|
48
|
-
LTEQ_NAMED_RE = /\A([\w.]+)\s+(<=?)\s+:(\w+)\z/.freeze
|
56
|
+
LTEQ_NAMED_RE = /\A\s*([\w.]+)\s+(<=?)\s+:(\w+)\s*\z/.freeze
|
49
57
|
# column >= :value1 AND column <[=] :value2
|
50
|
-
RANGE_NAMED_RE = /\A([\w.]+)\s+>=\s+:(\w+)\s+AND\s+\1\s+(<=?)\s+:(\w+)\z/i.freeze
|
58
|
+
RANGE_NAMED_RE = /\A\s*([\w.]+)\s+>=\s+:(\w+)\s+AND\s+\1\s+(<=?)\s+:(\w+)\s*\z/i.freeze
|
51
59
|
|
52
60
|
minimum_target_ruby_version 2.6
|
53
61
|
minimum_target_rails_version 6.0
|
@@ -86,47 +94,63 @@ module RuboCop
|
|
86
94
|
|
87
95
|
# rubocop:disable Metrics
|
88
96
|
def extract_column_and_value(template_node, values_node)
|
89
|
-
value
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
97
|
+
case template_node.value
|
98
|
+
when GTEQ_ANONYMOUS_RE
|
99
|
+
lhs = values_node[0]
|
100
|
+
operator = '..'
|
101
|
+
when LTEQ_ANONYMOUS_RE
|
102
|
+
if target_ruby_version >= 2.7
|
103
|
+
operator = range_operator(Regexp.last_match(2))
|
104
|
+
rhs = values_node[0]
|
105
|
+
end
|
106
|
+
when RANGE_ANONYMOUS_RE
|
107
|
+
if values_node.size >= 2
|
108
|
+
lhs = values_node[0]
|
109
|
+
operator = range_operator(Regexp.last_match(2))
|
110
|
+
rhs = values_node[1]
|
111
|
+
end
|
112
|
+
when GTEQ_NAMED_RE
|
113
|
+
value_node = values_node[0]
|
103
114
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
115
|
+
if value_node.hash_type?
|
116
|
+
pair = find_pair(value_node, Regexp.last_match(2))
|
117
|
+
lhs = pair.value
|
118
|
+
operator = '..'
|
119
|
+
end
|
120
|
+
when LTEQ_NAMED_RE
|
121
|
+
value_node = values_node[0]
|
122
|
+
|
123
|
+
if value_node.hash_type?
|
124
|
+
pair = find_pair(value_node, Regexp.last_match(2))
|
125
|
+
if pair && target_ruby_version >= 2.7
|
126
|
+
operator = range_operator(Regexp.last_match(2))
|
127
|
+
rhs = pair.value
|
117
128
|
end
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
129
|
+
end
|
130
|
+
when RANGE_NAMED_RE
|
131
|
+
value_node = values_node[0]
|
132
|
+
|
133
|
+
if value_node.hash_type?
|
134
|
+
pair1 = find_pair(value_node, Regexp.last_match(2))
|
135
|
+
pair2 = find_pair(value_node, Regexp.last_match(4))
|
136
|
+
|
137
|
+
if pair1 && pair2
|
138
|
+
lhs = pair1.value
|
139
|
+
operator = range_operator(Regexp.last_match(3))
|
140
|
+
rhs = pair2.value
|
126
141
|
end
|
127
142
|
end
|
143
|
+
end
|
128
144
|
|
129
|
-
|
145
|
+
if lhs
|
146
|
+
lhs_source = parentheses_needed?(lhs) ? "(#{lhs.source})" : lhs.source
|
147
|
+
end
|
148
|
+
|
149
|
+
if rhs
|
150
|
+
rhs_source = parentheses_needed?(rhs) ? "(#{rhs.source})" : rhs.source
|
151
|
+
end
|
152
|
+
|
153
|
+
[Regexp.last_match(1), "#{lhs_source}#{operator}#{rhs_source}"] if operator
|
130
154
|
end
|
131
155
|
# rubocop:enable Metrics
|
132
156
|
|
@@ -151,6 +175,23 @@ module RuboCop
|
|
151
175
|
"#{method_name}(#{column}: #{value})"
|
152
176
|
end
|
153
177
|
end
|
178
|
+
|
179
|
+
def parentheses_needed?(node)
|
180
|
+
!parentheses_not_needed?(node)
|
181
|
+
end
|
182
|
+
|
183
|
+
def parentheses_not_needed?(node)
|
184
|
+
node.variable? ||
|
185
|
+
node.literal? ||
|
186
|
+
node.reference? ||
|
187
|
+
node.const_type? ||
|
188
|
+
node.begin_type? ||
|
189
|
+
parenthesized_call_node?(node)
|
190
|
+
end
|
191
|
+
|
192
|
+
def parenthesized_call_node?(node)
|
193
|
+
node.call_type? && (node.arguments.empty? || node.parenthesized_call?)
|
194
|
+
end
|
154
195
|
end
|
155
196
|
end
|
156
197
|
end
|
@@ -46,6 +46,7 @@ require_relative 'rails/duration_arithmetic'
|
|
46
46
|
require_relative 'rails/dynamic_find_by'
|
47
47
|
require_relative 'rails/eager_evaluation_log_message'
|
48
48
|
require_relative 'rails/enum_hash'
|
49
|
+
require_relative 'rails/enum_syntax'
|
49
50
|
require_relative 'rails/enum_uniqueness'
|
50
51
|
require_relative 'rails/env_local'
|
51
52
|
require_relative 'rails/environment_comparison'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.26.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bozhidar Batsov
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2024-
|
13
|
+
date: 2024-08-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -46,7 +46,7 @@ dependencies:
|
|
46
46
|
requirements:
|
47
47
|
- - ">="
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version: 1.
|
49
|
+
version: 1.52.0
|
50
50
|
- - "<"
|
51
51
|
- !ruby/object:Gem::Version
|
52
52
|
version: '2.0'
|
@@ -56,7 +56,7 @@ dependencies:
|
|
56
56
|
requirements:
|
57
57
|
- - ">="
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: 1.
|
59
|
+
version: 1.52.0
|
60
60
|
- - "<"
|
61
61
|
- !ruby/object:Gem::Version
|
62
62
|
version: '2.0'
|
@@ -140,6 +140,7 @@ files:
|
|
140
140
|
- lib/rubocop/cop/rails/dynamic_find_by.rb
|
141
141
|
- lib/rubocop/cop/rails/eager_evaluation_log_message.rb
|
142
142
|
- lib/rubocop/cop/rails/enum_hash.rb
|
143
|
+
- lib/rubocop/cop/rails/enum_syntax.rb
|
143
144
|
- lib/rubocop/cop/rails/enum_uniqueness.rb
|
144
145
|
- lib/rubocop/cop/rails/env_local.rb
|
145
146
|
- lib/rubocop/cop/rails/environment_comparison.rb
|
@@ -246,7 +247,7 @@ metadata:
|
|
246
247
|
homepage_uri: https://docs.rubocop.org/rubocop-rails/
|
247
248
|
changelog_uri: https://github.com/rubocop/rubocop-rails/blob/master/CHANGELOG.md
|
248
249
|
source_code_uri: https://github.com/rubocop/rubocop-rails/
|
249
|
-
documentation_uri: https://docs.rubocop.org/rubocop-rails/2.
|
250
|
+
documentation_uri: https://docs.rubocop.org/rubocop-rails/2.26/
|
250
251
|
bug_tracker_uri: https://github.com/rubocop/rubocop-rails/issues
|
251
252
|
rubygems_mfa_required: 'true'
|
252
253
|
post_install_message:
|
@@ -264,7 +265,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
264
265
|
- !ruby/object:Gem::Version
|
265
266
|
version: '0'
|
266
267
|
requirements: []
|
267
|
-
rubygems_version: 3.5.
|
268
|
+
rubygems_version: 3.5.11
|
268
269
|
signing_key:
|
269
270
|
specification_version: 4
|
270
271
|
summary: Automatic Rails code style checking tool.
|