rubocop-rails 2.25.0 → 2.26.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cad5e6d0c6f188b6ff87adb04fbfb73c7c574b901de45256eb7fa6e50b7a136d
4
- data.tar.gz: c8ab8f1d1c3284054af5847231dd996df24ee5441f638f95bc7e32420a3baf04
3
+ metadata.gz: c18cefb961b3100a871e2411c7a029244eda6998d793e3ece62aef1adcd4c571
4
+ data.tar.gz: 24cde448ef91e689e05a2357c1aa0cbcc4d61630f1bb6e7fcaf8999ff7678105
5
5
  SHA512:
6
- metadata.gz: ea0ebe45e988d115aa45d7bdba2b728b0583e1e2d996d43af424eb8abc17ced35b528741fcc47b8b258de52edd2fb7bc1c92dd609b49ddeeaaaf26bce123e783
7
- data.tar.gz: 7eb06cfce19f2a4ba78c6d571c65cf386e211b48ca2c9b5b9ca436ec9055e49f9737cbb3f9ce1b44ab432de87121295439effc59f093446cea88199bd08ba8a4
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
  [![Gem Version](https://badge.fury.io/rb/rubocop-rails.svg)](https://badge.fury.io/rb/rubocop-rails)
4
- [![CircleCI](https://circleci.com/gh/rubocop/rubocop-rails.svg?style=svg)](https://circleci.com/gh/rubocop/rubocop-rails)
4
+ [![CI](https://github.com/rubocop/rubocop-rails/actions/workflows/test.yml/badge.svg)](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.7'
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.10'
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.compact.any? do |render_candidate|
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(node.block_node.body)
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.reject!(&:blank?) # Same behavior as `ActionController::Parameters#compact_blank!`
35
- # collection.reject! { |_k, v| v.blank? } # Same behavior as `ActionController::Parameters#compact_blank!`
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 reject!].freeze
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 :reject!})
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 :reject!}
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
- if (arguments, receiver_in_block = reject_with_block?(node.parent))
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 'strict'
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 'to_time_in_current_zone' are reported as warning.
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 :enum?, <<~PATTERN
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
- enum?(node) do |pairs|
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: format(MSG, enum: enum_name(key))) do |corrector|
41
- hash = array.children.each_with_index.map do |elem, index|
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 |pairs|
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
- items = args.values
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
@@ -52,7 +52,7 @@ module RuboCop
52
52
  (send
53
53
  nil?
54
54
  {#{FILTERS.join(' ')}}
55
- _
55
+ ...
56
56
  $_)
57
57
  PATTERN
58
58
 
@@ -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 different enforcement modes. When the `EnforcedStyle`
13
- # is `conservative` (the default) then only calls to `pluck` on a constant
14
- # (i.e. a model class) in the `where` is used as offenses.
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 the `EnforcedStyle` is `aggressive` then all calls to `pluck` in the
18
- # `where` is used as offenses. This may lead to false positives
19
- # as the cop cannot replace to `select` between calls to `pluck` on an
20
- # `ActiveRecord::Relation` instance vs a call to `pluck` on an `Array` 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
- SINGULAR_DURATION_METHODS = { second: :seconds,
21
- minute: :minutes,
22
- hour: :hours,
23
- day: :days,
24
- week: :weeks,
25
- fortnight: :fortnights,
26
- month: :months,
27
- year: :years }.freeze
28
-
29
- RESTRICT_ON_SEND = SINGULAR_DURATION_METHODS.keys + SINGULAR_DURATION_METHODS.values
30
-
31
- PLURAL_DURATION_METHODS = SINGULAR_DURATION_METHODS.invert.freeze
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
- SINGULAR_DURATION_METHODS.fetch(method_name.to_sym).to_s
103
+ SINGULAR_METHODS.fetch(method_name.to_sym).to_s
90
104
  end
91
105
 
92
106
  def singularize(method_name)
93
- PLURAL_DURATION_METHODS.fetch(method_name.to_sym).to_s
107
+ PLURAL_METHODS.fetch(method_name.to_sym).to_s
94
108
  end
95
109
 
96
110
  def duration_method?(method_name)
97
- SINGULAR_DURATION_METHODS.key?(method_name) || PLURAL_DURATION_METHODS.key?(method_name)
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?(node)
57
- (node && node.value.value == 'text/plain') ||
58
- (!node && !cop_config['ContentTypeCompatibility'])
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) :glob ...)
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, method)
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, method)
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}.#{method}(#{argument})"
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 `where(attribute: value)`.
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
- column_and_value = extract_column_and_value(template_node, value_node)
48
- return unless column_and_value
52
+ column, value = extract_column_and_value(template_node, value_node)
53
+ return unless value
49
54
 
50
- good_method = build_good_method(*column_and_value)
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.source
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
- "where(#{table}: { #{column}: #{value} })"
100
+ "#{method_name}(#{table}: { #{column}: #{value} })"
96
101
  else
97
- "where(#{column}: #{value})"
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
- column_and_value = extract_column_and_value(template_node, value_node)
47
- return unless column_and_value
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, *column_and_value)
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.source
75
+ value_node&.source
76
76
  when NOT_EQ_NAMED_RE, NOT_IN_NAMED_RE
77
- return unless value_node.hash_type?
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
- case template_node.value
91
- when GTEQ_ANONYMOUS_RE
92
- "#{values_node[0].source}.."
93
- when LTEQ_ANONYMOUS_RE
94
- range_operator = range_operator(Regexp.last_match(2))
95
- "#{range_operator}#{values_node[0].source}" if target_ruby_version >= 2.7
96
- when RANGE_ANONYMOUS_RE
97
- if values_node.size >= 2
98
- range_operator = range_operator(Regexp.last_match(2))
99
- "#{values_node[0].source}#{range_operator}#{values_node[1].source}"
100
- end
101
- when GTEQ_NAMED_RE
102
- value_node = values_node[0]
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
- if value_node.hash_type?
105
- pair = find_pair(value_node, Regexp.last_match(2))
106
- "#{pair.value.source}.." if pair
107
- end
108
- when LTEQ_NAMED_RE
109
- value_node = values_node[0]
110
-
111
- if value_node.hash_type?
112
- pair = find_pair(value_node, Regexp.last_match(2))
113
- if pair && target_ruby_version >= 2.7
114
- range_operator = range_operator(Regexp.last_match(2))
115
- "#{range_operator}#{pair.value.source}"
116
- end
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
- when RANGE_NAMED_RE
119
- value_node = values_node[0]
120
-
121
- if value_node.hash_type?
122
- range_operator = range_operator(Regexp.last_match(3))
123
- pair1 = find_pair(value_node, Regexp.last_match(2))
124
- pair2 = find_pair(value_node, Regexp.last_match(4))
125
- "#{pair1.value.source}#{range_operator}#{pair2.value.source}" if pair1 && pair2
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
- [Regexp.last_match(1), value] if value
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'
@@ -178,7 +178,7 @@ module RuboCop
178
178
  attr_reader :table_name
179
179
 
180
180
  def initialize(node)
181
- super(node)
181
+ super
182
182
 
183
183
  @table_name = node.first_argument.value
184
184
  @columns, @expression = build_columns_or_expr(node.arguments[1])
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Rails
5
5
  # This module holds the RuboCop Rails version information.
6
6
  module Version
7
- STRING = '2.25.0'
7
+ STRING = '2.26.0'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
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.25.0
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-05-17 00:00:00.000000000 Z
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.33.0
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.33.0
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.25/
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.3
268
+ rubygems_version: 3.5.11
268
269
  signing_key:
269
270
  specification_version: 4
270
271
  summary: Automatic Rails code style checking tool.