rubocop-rails 2.21.1 → 2.22.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: 15c354794d638a6c49b01b74961ea1d55f5541f3a8b653ef68d9a824e042a100
4
- data.tar.gz: 3a04d9449daac9515b0be93d1394b1aa260748ab6bc57517cf3b90217f58ec88
3
+ metadata.gz: 04c91e90b833ac948b90250b28d11aa4800aae35a8b30a6e70f80f21b69f6f3b
4
+ data.tar.gz: ff6f1a9257f9bb1e1a49e7ff099de9ae138cea69ae3b50c02cccad5872d2040a
5
5
  SHA512:
6
- metadata.gz: '01167387c8fcee795769724ded84c4eaa373e2d2d936e8a2b0afd5f4bcb239aafd299b1c302d5a029b7ae72f2990290f2f6de1d317d1cb163f707d5f0ee91635'
7
- data.tar.gz: 61d8f93f138aceb3c8e7a8ef03604d5de870b8328e452d34c03f77612681e9846ac7a4f0ed127a48806bb2bfad055e6ccebe9d3393e82e0f4e01ea88a1d870a6
6
+ metadata.gz: f4033f539ef9bf1282b0ac5a81d026c683dd618f7a367b59e03117be1092a790f4df71a26b304ea4399c484d9a0c2af31987d8d9f793b822f570ab24df614477
7
+ data.tar.gz: 0aec6e0248f966e706460fa9796831d2617f04b371a1d37531e9dd52938278e59802b804d7320aec7bfd3677cfe01f55600cb4a5fd07c3c2e4ff68542e2a78fa
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
@@ -72,7 +74,9 @@ module YourCoolApp
72
74
  class Application < Rails::Application
73
75
  config.generators.after_generate do |files|
74
76
  parsable_files = files.filter { |file| file.end_with?('.rb') }
75
- system("bundle exec rubocop -A --fail-level=E #{parsable_files.shelljoin}", exception: true)
77
+ unless parsable_files.empty?
78
+ system("bundle exec rubocop -A --fail-level=E #{parsable_files.shelljoin}", exception: true)
79
+ end
76
80
  end
77
81
  end
78
82
  end
data/config/default.yml CHANGED
@@ -95,8 +95,9 @@ Rails/ActionControllerTestCase:
95
95
 
96
96
  Rails/ActionFilter:
97
97
  Description: 'Enforces consistent use of action filter methods.'
98
- Enabled: true
98
+ Enabled: false
99
99
  VersionAdded: '0.19'
100
+ VersionChanged: '2.22'
100
101
  EnforcedStyle: action
101
102
  SupportedStyles:
102
103
  - action
@@ -338,7 +339,6 @@ Rails/Date:
338
339
 
339
340
  Rails/DefaultScope:
340
341
  Description: 'Avoid use of `default_scope`.'
341
- StyleGuide: 'https://rails.rubystyle.guide#avoid-default-scope'
342
342
  Enabled: false
343
343
  VersionAdded: '2.7'
344
344
 
@@ -430,6 +430,11 @@ Rails/EnumUniqueness:
430
430
  Include:
431
431
  - app/models/**/*.rb
432
432
 
433
+ Rails/EnvLocal:
434
+ Description: 'Use `Rails.env.local?` instead of `Rails.env.development? || Rails.env.test?`.'
435
+ Enabled: pending
436
+ VersionAdded: '2.22'
437
+
433
438
  Rails/EnvironmentComparison:
434
439
  Description: "Favor `Rails.env.production?` over `Rails.env == 'production'`."
435
440
  Enabled: true
@@ -690,6 +695,9 @@ Rails/NotNullColumn:
690
695
  Enabled: true
691
696
  VersionAdded: '0.43'
692
697
  VersionChanged: '2.20'
698
+ Database: null
699
+ SupportedDatabases:
700
+ - mysql
693
701
  Include:
694
702
  - db/**/*.rb
695
703
 
@@ -801,6 +809,9 @@ Rails/RedundantActiveRecordAllMethod:
801
809
  StyleGuide: 'https://rails.rubystyle.guide/#redundant-all'
802
810
  Enabled: pending
803
811
  Safe: false
812
+ AllowedReceivers:
813
+ - ActionMailer::Preview
814
+ - ActiveSupport::TimeZone
804
815
  VersionAdded: '2.21'
805
816
 
806
817
  Rails/RedundantAllowNil:
@@ -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'
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
@@ -102,7 +102,7 @@ module RuboCop
102
102
  end
103
103
 
104
104
  # Internal helper class to hold match data
105
- Captures = Struct.new(
105
+ Captures = ::Struct.new(
106
106
  :transformed_argname,
107
107
  :transforming_body_expr
108
108
  ) do
@@ -112,7 +112,7 @@ module RuboCop
112
112
  end
113
113
 
114
114
  # Internal helper class to hold autocorrect data
115
- Autocorrection = Struct.new(:match, :block_node, :leading, :trailing) do
115
+ Autocorrection = ::Struct.new(:match, :block_node, :leading, :trailing) do
116
116
  def self.from_each_with_object(node, match)
117
117
  new(match, node, 0, 0)
118
118
  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 Rals 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
@@ -14,7 +14,7 @@ module RuboCop
14
14
  # automatically detect an adapter from `development` environment
15
15
  # in `config/database.yml` or the environment variable `DATABASE_URL`
16
16
  # when the `Database` option is not set.
17
- # If the adapter is not `mysql2` or `postgresql`,
17
+ # If the adapter is not `mysql2`, `trilogy`, or `postgresql`,
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
@@ -31,10 +31,11 @@ module RuboCop
31
31
  time
32
32
  ].to_set.freeze
33
33
 
34
- # Generated from `ActiveRecord::AttributeMethods.dangerous_attribute_methods` on activerecord 7.0.5.
34
+ # Generated from `ActiveRecord::AttributeMethods.dangerous_attribute_methods` on activerecord 7.1.0.
35
35
  # rubocop:disable Metrics/CollectionLiteralLength
36
36
  DANGEROUS_COLUMN_NAMES = %w[
37
37
  __callbacks
38
+ __id__
38
39
  _assign_attribute
39
40
  _assign_attributes
40
41
  _before_commit_callbacks
@@ -195,11 +196,13 @@ module RuboCop
195
196
  changes_to_save
196
197
  check_record_limit
197
198
  ciphertext_for
199
+ class
198
200
  clear_attribute_change
199
201
  clear_attribute_changes
200
202
  clear_changes_information
201
203
  clear_timestamp_attributes
202
204
  clear_transaction_record_state
205
+ clone
203
206
  collection_cache_versioning
204
207
  column_for_attribute
205
208
  committed
@@ -227,6 +230,7 @@ module RuboCop
227
230
  destroyed
228
231
  destroyed_by_association
229
232
  destroyed_by_association=
233
+ dup
230
234
  each_counter_cached_associations
231
235
  encode_with
232
236
  encrypt
@@ -243,7 +247,9 @@ module RuboCop
243
247
  find_parameter_position
244
248
  forget_attribute_assignments
245
249
  format_for_inspect
250
+ freeze
246
251
  from_json
252
+ frozen?
247
253
  halted_callback_hook
248
254
  has_attribute
249
255
  has_changes_to_save
@@ -252,6 +258,7 @@ module RuboCop
252
258
  has_encrypted_attributes
253
259
  has_encrypted_rich_texts
254
260
  has_transactional_callbacks
261
+ hash
255
262
  id
256
263
  id_before_type_cast
257
264
  id_for_database
@@ -283,6 +290,7 @@ module RuboCop
283
290
  new_record
284
291
  no_touching
285
292
  normalize_reflection_attribute
293
+ object_id
286
294
  partial_inserts
287
295
  partial_updates
288
296
  perform_validations
@@ -20,6 +20,15 @@ module RuboCop
20
20
  # belongs_to :bar
21
21
  # has_one :foo
22
22
  #
23
+ # # bad
24
+ # belongs_to :foo, class_name: 'Foo'
25
+ # belongs_to :bar, class_name: 'Foo'
26
+ # has_one :baz
27
+ #
28
+ # # good
29
+ # belongs_to :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,75 @@ module RuboCop
27
36
  include ActiveRecordHelper
28
37
 
29
38
  MSG = "Association `%<name>s` is defined multiple times. Don't repeat associations."
39
+ MSG_CLASS_NAME = "Association `class_name: %<name>s` is defined multiple times. Don't repeat associations."
30
40
 
31
41
  def_node_matcher :association, <<~PATTERN
32
- (send nil? {:belongs_to :has_one :has_many :has_and_belongs_to_many} ({sym str} $_) ...)
42
+ (send nil? {:belongs_to :has_one :has_many :has_and_belongs_to_many} ({sym str} $_) $...)
43
+ PATTERN
44
+
45
+ def_node_matcher :class_name, <<~PATTERN
46
+ (hash (pair (sym :class_name) $_))
33
47
  PATTERN
34
48
 
35
49
  def on_class(class_node)
36
50
  return unless active_record?(class_node.parent_class)
37
51
 
38
- offenses(class_node).each do |name, nodes|
39
- nodes.each do |node|
40
- add_offense(node, message: format(MSG, name: name)) do |corrector|
41
- next if same_line?(nodes.last, node)
52
+ association_nodes = association_nodes(class_node)
42
53
 
43
- corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
44
- end
45
- end
54
+ duplicated_association_name_nodes(association_nodes).each do |name, nodes|
55
+ register_offense(name, nodes, MSG)
56
+ end
57
+
58
+ duplicated_class_name_nodes(association_nodes).each do |class_name, nodes|
59
+ register_offense(class_name, nodes, MSG_CLASS_NAME)
46
60
  end
47
61
  end
48
62
 
49
63
  private
50
64
 
51
- def offenses(class_node)
52
- class_send_nodes(class_node).select { |node| association(node) }
53
- .group_by { |node| association(node).to_sym }
54
- .select { |_, nodes| nodes.length > 1 }
65
+ def register_offense(name, nodes, message_template)
66
+ nodes.each do |node|
67
+ add_offense(node, message: format(message_template, name: name)) do |corrector|
68
+ next if same_line?(nodes.last, node)
69
+
70
+ corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
71
+ end
72
+ end
73
+ end
74
+
75
+ def association_nodes(class_node)
76
+ class_send_nodes(class_node).select do |node|
77
+ association(node)&.first
78
+ end
79
+ end
80
+
81
+ def duplicated_association_name_nodes(association_nodes)
82
+ grouped_associations = association_nodes.group_by do |node|
83
+ association(node).first.to_sym
84
+ end
85
+
86
+ leave_duplicated_association(grouped_associations)
87
+ end
88
+
89
+ def duplicated_class_name_nodes(association_nodes)
90
+ grouped_associations = association_nodes.group_by do |node|
91
+ arguments = association(node).last
92
+ next unless arguments.count == 1
93
+
94
+ if (class_name = class_name(arguments.first))
95
+ class_name.source
96
+ end
97
+ end
98
+
99
+ grouped_associations.delete(nil)
100
+
101
+ leave_duplicated_association(grouped_associations)
102
+ end
103
+
104
+ def leave_duplicated_association(grouped_associations)
105
+ grouped_associations.select do |_, nodes|
106
+ nodes.length > 1
107
+ end
55
108
  end
56
109
  end
57
110
  end
@@ -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
@@ -60,7 +60,7 @@ module RuboCop
60
60
  (block
61
61
  (send nil? :with_options
62
62
  (hash $...))
63
- (args) ...)
63
+ (args _?) ...)
64
64
  PATTERN
65
65
 
66
66
  def_node_matcher :association_extension_block?, <<~PATTERN
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Checks for use of the helper methods which reference
7
7
  # instance variables.
8
8
  #
9
- # Relying on instance variables makes it difficult to re-use helper
9
+ # Relying on instance variables makes it difficult to reuse helper
10
10
  # methods.
11
11
  #
12
12
  # If it seems awkward to explicitly pass in each dependent
@@ -3,8 +3,13 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # Checks for add_column call with NOT NULL constraint
7
- # in migration file.
6
+ # Checks for add_column call with NOT NULL constraint in migration file.
7
+ #
8
+ # `TEXT` can have default values in PostgreSQL, but not in MySQL.
9
+ # It will automatically detect an adapter from `development` environment
10
+ # in `config/database.yml` or the environment variable `DATABASE_URL`
11
+ # when the `Database` option is not set. If the database is MySQL,
12
+ # this cop ignores offenses for the `TEXT`.
8
13
  #
9
14
  # @example
10
15
  # # bad
@@ -17,6 +22,8 @@ module RuboCop
17
22
  # add_reference :products, :category
18
23
  # add_reference :products, :category, null: false, default: 1
19
24
  class NotNullColumn < Base
25
+ include DatabaseTypeResolvable
26
+
20
27
  MSG = 'Do not add a NOT NULL column without a default value.'
21
28
  RESTRICT_ON_SEND = %i[add_column add_reference].freeze
22
29
 
@@ -45,7 +52,10 @@ module RuboCop
45
52
 
46
53
  def check_add_column(node)
47
54
  add_not_null_column?(node) do |type, pairs|
48
- return if type.respond_to?(:value) && (type.value == :virtual || type.value == 'virtual')
55
+ if type.respond_to?(:value)
56
+ return if type.value == :virtual || type.value == 'virtual'
57
+ return if (type.value == :text || type.value == 'text') && database == MYSQL
58
+ end
49
59
 
50
60
  check_pairs(pairs)
51
61
  end
@@ -23,6 +23,7 @@ module RuboCop
23
23
 
24
24
  MSG = "Do not write to stdout. Use Rails's logger if you want to log."
25
25
  RESTRICT_ON_SEND = %i[ap p pp pretty_print print puts binwrite syswrite write write_nonblock].freeze
26
+ ALLOWED_TYPES = %i[send csend block numblock].freeze
26
27
 
27
28
  def_node_matcher :output?, <<~PATTERN
28
29
  (send nil? {:ap :p :pp :pretty_print :print :puts} ...)
@@ -39,8 +40,8 @@ module RuboCop
39
40
  PATTERN
40
41
 
41
42
  def on_send(node)
42
- return if node.parent&.call_type?
43
- return unless output?(node) || io_output?(node)
43
+ return if ALLOWED_TYPES.include?(node.parent&.type)
44
+ return if !output?(node) && !io_output?(node)
44
45
 
45
46
  range = offense_range(node)
46
47
 
@@ -20,19 +20,34 @@ module RuboCop
20
20
  # User.order(:created_at)
21
21
  # users.where(id: ids)
22
22
  # user.articles.order(:created_at)
23
+ #
24
+ # @example AllowedReceivers: ['ActionMailer::Preview', 'ActiveSupport::TimeZone'] (default)
25
+ # # good
26
+ # ActionMailer::Preview.all.first
27
+ # ActiveSupport::TimeZone.all.first
23
28
  class RedundantActiveRecordAllMethod < Base
24
29
  include ActiveRecordHelper
30
+ include AllowedReceivers
31
+ include RangeHelp
25
32
  extend AutoCorrector
26
33
 
27
34
  MSG = 'Redundant `all` detected.'
28
35
 
29
36
  RESTRICT_ON_SEND = [:all].freeze
30
37
 
31
- # Defined methods in `ActiveRecord::Querying::QUERYING_METHODS` on activerecord 7.0.5.
38
+ # Defined methods in `ActiveRecord::Querying::QUERYING_METHODS` on activerecord 7.1.0.
32
39
  QUERYING_METHODS = %i[
33
40
  and
34
41
  annotate
35
42
  any?
43
+ async_average
44
+ async_count
45
+ async_ids
46
+ async_maximum
47
+ async_minimum
48
+ async_pick
49
+ async_pluck
50
+ async_sum
36
51
  average
37
52
  calculate
38
53
  count
@@ -102,6 +117,7 @@ module RuboCop
102
117
  preload
103
118
  readonly
104
119
  references
120
+ regroup
105
121
  reorder
106
122
  reselect
107
123
  rewhere
@@ -123,23 +139,68 @@ module RuboCop
123
139
  unscope
124
140
  update_all
125
141
  where
142
+ with
126
143
  without
127
144
  ].to_set.freeze
128
145
 
146
+ POSSIBLE_ENUMERABLE_BLOCK_METHODS = %i[any? count find none? one? select sum].freeze
147
+
129
148
  def_node_matcher :followed_by_query_method?, <<~PATTERN
130
149
  (send (send _ :all) QUERYING_METHODS ...)
131
150
  PATTERN
132
151
 
133
152
  def on_send(node)
134
- return unless followed_by_query_method?(node.parent)
135
- return if node.receiver.nil? && !inherit_active_record_base?(node)
153
+ return if !followed_by_query_method?(node.parent) || possible_enumerable_block_method?(node)
154
+ return if node.receiver ? allowed_receiver?(node.receiver) : !inherit_active_record_base?(node)
136
155
 
137
- range_of_all_method = node.loc.selector
156
+ range_of_all_method = offense_range(node)
138
157
  add_offense(range_of_all_method) do |collector|
139
158
  collector.remove(range_of_all_method)
140
159
  collector.remove(node.parent.loc.dot)
141
160
  end
142
161
  end
162
+
163
+ private
164
+
165
+ def possible_enumerable_block_method?(node)
166
+ parent = node.parent
167
+ return false unless POSSIBLE_ENUMERABLE_BLOCK_METHODS.include?(parent.method_name)
168
+
169
+ parent.parent&.block_type? || parent.parent&.numblock_type? || parent.first_argument&.block_pass_type?
170
+ end
171
+
172
+ def offense_range(node)
173
+ range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
174
+ end
175
+
176
+ # TODO: In the future, please support only RuboCop 1.52+ and use `RuboCop::Cop::AllowedReceivers`:
177
+ # https://github.com/rubocop/rubocop/blob/v1.52.0/lib/rubocop/cop/mixin/allowed_receivers.rb
178
+ # At that time, this duplicated module implementation can be removed.
179
+ module AllowedReceivers
180
+ def allowed_receiver?(receiver)
181
+ receiver_name = receiver_name(receiver)
182
+
183
+ allowed_receivers.include?(receiver_name)
184
+ end
185
+
186
+ def receiver_name(receiver)
187
+ return receiver_name(receiver.receiver) if receiver.receiver && !receiver.receiver.const_type?
188
+
189
+ if receiver.send_type?
190
+ if receiver.receiver
191
+ "#{receiver_name(receiver.receiver)}.#{receiver.method_name}"
192
+ else
193
+ receiver.method_name.to_s
194
+ end
195
+ else
196
+ receiver.source
197
+ end
198
+ end
199
+
200
+ def allowed_receivers
201
+ cop_config.fetch('AllowedReceivers', [])
202
+ end
203
+ end
143
204
  end
144
205
  end
145
206
  end
@@ -53,6 +53,12 @@ module RuboCop
53
53
  # @example source that matches - by a foreign key
54
54
  # validates :user_id, presence: true
55
55
  #
56
+ # @example source that DOES NOT match - if condition
57
+ # validates :user_id, presence: true, if: condition
58
+ #
59
+ # @example source that DOES NOT match - unless condition
60
+ # validates :user_id, presence: true, unless: condition
61
+ #
56
62
  # @example source that DOES NOT match - strict validation
57
63
  # validates :user_id, presence: true, strict: true
58
64
  #
@@ -65,6 +71,7 @@ module RuboCop
65
71
  $[
66
72
  (hash <$(pair (sym :presence) true) ...>) # presence: true
67
73
  !(hash <$(pair (sym :strict) {true const}) ...>) # strict: true
74
+ !(hash <$(pair (sym {:if :unless}) _) ...>) # if: some_condition or unless: some_condition
68
75
  ]
69
76
  )
70
77
  PATTERN
@@ -235,10 +235,10 @@ module RuboCop
235
235
 
236
236
  def in_condition_or_compound_boolean?(node)
237
237
  node = node.block_node || node
238
- parent = node.parent
238
+ parent = node.each_ancestor.find { |ancestor| !ancestor.begin_type? }
239
239
  return false unless parent
240
240
 
241
- operator_or_single_negative?(parent) || (conditional?(parent) && node == parent.condition)
241
+ operator_or_single_negative?(parent) || (conditional?(parent) && node == deparenthesize(parent.condition))
242
242
  end
243
243
 
244
244
  def operator_or_single_negative?(node)
@@ -249,6 +249,11 @@ module RuboCop
249
249
  parent.if_type? || parent.case_type?
250
250
  end
251
251
 
252
+ def deparenthesize(node)
253
+ node = node.children.last while node.begin_type?
254
+ node
255
+ end
256
+
252
257
  def checked_immediately?(node)
253
258
  node.parent && call_to_persisted?(node.parent)
254
259
  end
@@ -51,10 +51,13 @@ module RuboCop
51
51
  end
52
52
  end
53
53
 
54
+ # rubocop:disable Metrics/AbcSize
54
55
  def autocorrect(corrector, select_node, node, preferred_method)
55
- corrector.remove(select_node.loc.dot.begin.join(select_node.source_range.end))
56
+ corrector.remove(select_node.loc.dot || node.loc.dot)
57
+ corrector.remove(select_node.loc.selector.begin.join(select_node.source_range.end))
56
58
  corrector.replace(node.loc.selector.begin.join(node.source_range.end), preferred_method)
57
59
  end
60
+ # rubocop:enable Metrics/AbcSize
58
61
 
59
62
  def match_column_name?(select_candidate, column_name)
60
63
  return false unless select_candidate.arguments.one?
@@ -125,7 +125,7 @@ module RuboCop
125
125
 
126
126
  def uniqueness_part(node)
127
127
  pairs = node.arguments.last
128
- return unless pairs.hash_type?
128
+ return unless pairs&.hash_type?
129
129
 
130
130
  pairs.each_pair.find do |pair|
131
131
  next unless pair.key.sym_type? && pair.key.value == :uniqueness
@@ -86,7 +86,11 @@ module RuboCop
86
86
  end
87
87
 
88
88
  def environments
89
- cop_config['Environments']
89
+ @environments ||= begin
90
+ e = cop_config['Environments']
91
+ e += ['local'] if target_rails_version >= 7.1
92
+ e
93
+ end
90
94
  end
91
95
  end
92
96
  end
@@ -14,10 +14,9 @@ module RuboCop
14
14
  # render status: 100, plain: 'Ruby!'
15
15
  #
16
16
  # # good
17
- # render status: :continue
18
- # render status: 100
17
+ # head :continue
18
+ # head 100
19
19
  class UnusedRenderContent < Base
20
- extend AutoCorrector
21
20
  include RangeHelp
22
21
 
23
22
  MSG = 'Do not specify body content for a response with a non-content status code'
@@ -3,6 +3,7 @@
3
3
  require_relative 'mixin/active_record_helper'
4
4
  require_relative 'mixin/active_record_migrations_helper'
5
5
  require_relative 'mixin/class_send_node_helper'
6
+ require_relative 'mixin/database_type_resolvable'
6
7
  require_relative 'mixin/enforce_superclass'
7
8
  require_relative 'mixin/index_method'
8
9
  require_relative 'mixin/migrations_helper'
@@ -46,6 +47,7 @@ require_relative 'rails/dynamic_find_by'
46
47
  require_relative 'rails/eager_evaluation_log_message'
47
48
  require_relative 'rails/enum_hash'
48
49
  require_relative 'rails/enum_uniqueness'
50
+ require_relative 'rails/env_local'
49
51
  require_relative 'rails/environment_comparison'
50
52
  require_relative 'rails/environment_variable_access'
51
53
  require_relative 'rails/exit'
@@ -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.21.1'
7
+ STRING = '2.22.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.21.1
4
+ version: 2.22.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: 2023-09-14 00:00:00.000000000 Z
13
+ date: 2023-10-27 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -78,6 +78,7 @@ files:
78
78
  - lib/rubocop/cop/mixin/active_record_helper.rb
79
79
  - lib/rubocop/cop/mixin/active_record_migrations_helper.rb
80
80
  - lib/rubocop/cop/mixin/class_send_node_helper.rb
81
+ - lib/rubocop/cop/mixin/database_type_resolvable.rb
81
82
  - lib/rubocop/cop/mixin/enforce_superclass.rb
82
83
  - lib/rubocop/cop/mixin/index_method.rb
83
84
  - lib/rubocop/cop/mixin/migrations_helper.rb
@@ -120,6 +121,7 @@ files:
120
121
  - lib/rubocop/cop/rails/eager_evaluation_log_message.rb
121
122
  - lib/rubocop/cop/rails/enum_hash.rb
122
123
  - lib/rubocop/cop/rails/enum_uniqueness.rb
124
+ - lib/rubocop/cop/rails/env_local.rb
123
125
  - lib/rubocop/cop/rails/environment_comparison.rb
124
126
  - lib/rubocop/cop/rails/environment_variable_access.rb
125
127
  - lib/rubocop/cop/rails/exit.rb
@@ -223,7 +225,7 @@ metadata:
223
225
  homepage_uri: https://docs.rubocop.org/rubocop-rails/
224
226
  changelog_uri: https://github.com/rubocop/rubocop-rails/blob/master/CHANGELOG.md
225
227
  source_code_uri: https://github.com/rubocop/rubocop-rails/
226
- documentation_uri: https://docs.rubocop.org/rubocop-rails/2.21/
228
+ documentation_uri: https://docs.rubocop.org/rubocop-rails/2.22/
227
229
  bug_tracker_uri: https://github.com/rubocop/rubocop-rails/issues
228
230
  rubygems_mfa_required: 'true'
229
231
  post_install_message: