rubocop-rails 2.20.2 → 2.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -2
  3. data/config/default.yml +68 -8
  4. data/lib/rubocop/cop/mixin/database_type_resolvable.rb +66 -0
  5. data/lib/rubocop/cop/mixin/index_method.rb +2 -2
  6. data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +1 -1
  7. data/lib/rubocop/cop/rails/action_controller_test_case.rb +2 -2
  8. data/lib/rubocop/cop/rails/action_filter.rb +3 -0
  9. data/lib/rubocop/cop/rails/bulk_change_table.rb +5 -38
  10. data/lib/rubocop/cop/rails/dangerous_column_names.rb +447 -0
  11. data/lib/rubocop/cop/rails/date.rb +1 -1
  12. data/lib/rubocop/cop/rails/duplicate_association.rb +69 -12
  13. data/lib/rubocop/cop/rails/dynamic_find_by.rb +3 -3
  14. data/lib/rubocop/cop/rails/env_local.rb +46 -0
  15. data/lib/rubocop/cop/rails/file_path.rb +4 -1
  16. data/lib/rubocop/cop/rails/freeze_time.rb +1 -1
  17. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +1 -1
  18. data/lib/rubocop/cop/rails/helper_instance_variable.rb +1 -1
  19. data/lib/rubocop/cop/rails/http_status.rb +4 -3
  20. data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +63 -13
  21. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +7 -8
  22. data/lib/rubocop/cop/rails/not_null_column.rb +13 -3
  23. data/lib/rubocop/cop/rails/output.rb +3 -2
  24. data/lib/rubocop/cop/rails/rake_environment.rb +20 -4
  25. data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +207 -0
  26. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +7 -0
  27. data/lib/rubocop/cop/rails/reversible_migration.rb +1 -1
  28. data/lib/rubocop/cop/rails/root_pathname_methods.rb +38 -4
  29. data/lib/rubocop/cop/rails/save_bang.rb +9 -4
  30. data/lib/rubocop/cop/rails/schema_comment.rb +16 -10
  31. data/lib/rubocop/cop/rails/select_map.rb +78 -0
  32. data/lib/rubocop/cop/rails/time_zone.rb +12 -5
  33. data/lib/rubocop/cop/rails/transaction_exit_statement.rb +29 -10
  34. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +1 -1
  35. data/lib/rubocop/cop/rails/unknown_env.rb +5 -1
  36. data/lib/rubocop/cop/rails/unused_render_content.rb +67 -0
  37. data/lib/rubocop/cop/rails/where_exists.rb +0 -1
  38. data/lib/rubocop/cop/rails_cops.rb +6 -0
  39. data/lib/rubocop/rails/schema_loader.rb +1 -1
  40. data/lib/rubocop/rails/version.rb +1 -1
  41. data/lib/rubocop-rails.rb +8 -0
  42. metadata +9 -3
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Checks for uses of `select(:column_name)` with `map(&:column_name)`.
7
+ # These can be replaced with `pluck(:column_name)`.
8
+ #
9
+ # There also should be some performance improvement since it skips instantiating the model class for matches.
10
+ #
11
+ # @safety
12
+ # This cop is unsafe because the model might override the attribute getter.
13
+ # Additionally, the model's `after_initialize` hooks are skipped when using `pluck`.
14
+ #
15
+ # @example
16
+ # # bad
17
+ # Model.select(:column_name).map(&:column_name)
18
+ #
19
+ # # good
20
+ # Model.pluck(:column_name)
21
+ #
22
+ class SelectMap < Base
23
+ extend AutoCorrector
24
+
25
+ MSG = 'Use `%<preferred_method>s` instead of `select` with `%<map_method>s`.'
26
+
27
+ RESTRICT_ON_SEND = %i[map collect].freeze
28
+
29
+ def on_send(node)
30
+ return unless node.first_argument
31
+
32
+ column_name = node.first_argument.source.delete_prefix('&:')
33
+ return unless (select_node = find_select_node(node, column_name))
34
+
35
+ offense_range = select_node.loc.selector.begin.join(node.source_range.end)
36
+ preferred_method = "pluck(:#{column_name})"
37
+ message = format(MSG, preferred_method: preferred_method, map_method: node.method_name)
38
+
39
+ add_offense(offense_range, message: message) do |corrector|
40
+ autocorrect(corrector, select_node, node, preferred_method)
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def find_select_node(node, column_name)
47
+ node.descendants.detect do |select_candidate|
48
+ next if !select_candidate.send_type? || !select_candidate.method?(:select)
49
+
50
+ match_column_name?(select_candidate, column_name)
51
+ end
52
+ end
53
+
54
+ # rubocop:disable Metrics/AbcSize
55
+ def autocorrect(corrector, select_node, node, preferred_method)
56
+ corrector.remove(select_node.loc.dot || node.loc.dot)
57
+ corrector.remove(select_node.loc.selector.begin.join(select_node.source_range.end))
58
+ corrector.replace(node.loc.selector.begin.join(node.source_range.end), preferred_method)
59
+ end
60
+ # rubocop:enable Metrics/AbcSize
61
+
62
+ def match_column_name?(select_candidate, column_name)
63
+ return false unless select_candidate.arguments.one?
64
+ return false unless (first_argument = select_candidate.first_argument)
65
+
66
+ argument = case select_candidate.first_argument.type
67
+ when :sym
68
+ first_argument.source.delete_prefix(':')
69
+ when :str
70
+ first_argument.value if first_argument.respond_to?(:value)
71
+ end
72
+
73
+ argument == column_name
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -21,6 +21,7 @@ module RuboCop
21
21
  # # bad
22
22
  # Time.now
23
23
  # Time.parse('2015-03-02T19:05:37')
24
+ # '2015-03-02T19:05:37'.to_time
24
25
  #
25
26
  # # good
26
27
  # Time.current
@@ -44,19 +45,17 @@ module RuboCop
44
45
  extend AutoCorrector
45
46
 
46
47
  MSG = 'Do not use `%<current>s` without zone. Use `%<prefer>s` instead.'
47
-
48
48
  MSG_ACCEPTABLE = 'Do not use `%<current>s` without zone. Use one of %<prefer>s instead.'
49
-
50
49
  MSG_LOCALTIME = 'Do not use `Time.localtime` without offset or zone.'
50
+ MSG_STRING_TO_TIME = 'Do not use `String#to_time` without zone. Use `Time.zone.parse` instead.'
51
51
 
52
52
  GOOD_METHODS = %i[zone zone_default find_zone find_zone!].freeze
53
-
54
53
  DANGEROUS_METHODS = %i[now local new parse at].freeze
55
-
56
54
  ACCEPTED_METHODS = %i[in_time_zone utc getlocal xmlschema iso8601 jisx0301 rfc3339 httpdate to_i to_f].freeze
57
-
58
55
  TIMEZONE_SPECIFIER = /([A-Za-z]|[+-]\d{2}:?\d{2})\z/.freeze
59
56
 
57
+ RESTRICT_ON_SEND = %i[to_time].freeze
58
+
60
59
  def on_const(node)
61
60
  mod, klass = *node
62
61
  # we should only check core classes
@@ -66,6 +65,14 @@ module RuboCop
66
65
  check_time_node(klass, node.parent) if klass == :Time
67
66
  end
68
67
 
68
+ def on_send(node)
69
+ return if !node.receiver&.str_type? || !node.method?(:to_time)
70
+
71
+ add_offense(node.loc.selector, message: MSG_STRING_TO_TIME) do |corrector|
72
+ corrector.replace(node, "Time.zone.parse(#{node.receiver.source})")
73
+ end
74
+ end
75
+
69
76
  private
70
77
 
71
78
  def autocorrect(corrector, node)
@@ -13,6 +13,8 @@ module RuboCop
13
13
  # error when rollback is desired, and to use `next` when commit is
14
14
  # desired.
15
15
  #
16
+ # If you are defining custom transaction methods, you can configure it with `TransactionMethods`.
17
+ #
16
18
  # @example
17
19
  # # bad
18
20
  # ApplicationRecord.transaction do
@@ -50,12 +52,16 @@ module RuboCop
50
52
  # # Commit
51
53
  # next if user.active?
52
54
  # end
55
+ #
56
+ # @example TransactionMethods: ["custom_transaction"]
57
+ # # bad
58
+ # CustomModel.custom_transaction do
59
+ # return if user.active?
60
+ # end
61
+ #
53
62
  class TransactionExitStatement < Base
54
- MSG = <<~MSG.chomp
55
- Exit statement `%<statement>s` is not allowed. Use `raise` (rollback) or `next` (commit).
56
- MSG
57
-
58
- RESTRICT_ON_SEND = %i[transaction with_lock].freeze
63
+ MSG = 'Exit statement `%<statement>s` is not allowed. Use `raise` (rollback) or `next` (commit).'
64
+ BUILT_IN_TRANSACTION_METHODS = %i[transaction with_lock].freeze
59
65
 
60
66
  def_node_search :exit_statements, <<~PATTERN
61
67
  ({return | break | send nil? :throw} ...)
@@ -70,10 +76,9 @@ module RuboCop
70
76
  PATTERN
71
77
 
72
78
  def on_send(node)
73
- return unless (parent = node.parent)
74
- return unless parent.block_type? && parent.body
79
+ return unless in_transaction_block?(node)
75
80
 
76
- exit_statements(parent.body).each do |statement_node|
81
+ exit_statements(node.parent.body).each do |statement_node|
77
82
  next if statement_node.break_type? && nested_block?(statement_node)
78
83
 
79
84
  statement = statement(statement_node)
@@ -85,6 +90,13 @@ module RuboCop
85
90
 
86
91
  private
87
92
 
93
+ def in_transaction_block?(node)
94
+ return false unless transaction_method_name?(node.method_name)
95
+ return false unless (parent = node.parent)
96
+
97
+ parent.block_type? && parent.body
98
+ end
99
+
88
100
  def statement(statement_node)
89
101
  if statement_node.return_type?
90
102
  'return'
@@ -96,9 +108,16 @@ module RuboCop
96
108
  end
97
109
 
98
110
  def nested_block?(statement_node)
99
- block_node = statement_node.ancestors.find(&:block_type?)
111
+ name = statement_node.ancestors.find(&:block_type?).children.first.method_name
112
+ !transaction_method_name?(name)
113
+ end
114
+
115
+ def transaction_method_name?(method_name)
116
+ BUILT_IN_TRANSACTION_METHODS.include?(method_name) || transaction_method?(method_name)
117
+ end
100
118
 
101
- RESTRICT_ON_SEND.none? { |name| block_node.method?(name) }
119
+ def transaction_method?(method_name)
120
+ cop_config.fetch('TransactionMethods', []).include?(method_name.to_s)
102
121
  end
103
122
  end
104
123
  end
@@ -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
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # If you try to render content along with a non-content status code (100-199, 204, 205, or 304),
7
+ # it will be dropped from the response.
8
+ #
9
+ # This cop checks for uses of `render` which specify both body content and a non-content status.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # render 'foo', status: :continue
14
+ # render status: 100, plain: 'Ruby!'
15
+ #
16
+ # # good
17
+ # head :continue
18
+ # head 100
19
+ class UnusedRenderContent < Base
20
+ include RangeHelp
21
+
22
+ MSG = 'Do not specify body content for a response with a non-content status code'
23
+ RESTRICT_ON_SEND = %i[render].freeze
24
+ NON_CONTENT_STATUS_CODES = Set[*100..199, 204, 205, 304] & ::Rack::Utils::SYMBOL_TO_STATUS_CODE.values
25
+ NON_CONTENT_STATUSES = Set[
26
+ *::Rack::Utils::SYMBOL_TO_STATUS_CODE.invert.fetch_values(*NON_CONTENT_STATUS_CODES)
27
+ ]
28
+ BODY_OPTIONS = Set[
29
+ :action,
30
+ :body,
31
+ :content_type,
32
+ :file,
33
+ :html,
34
+ :inline,
35
+ :json,
36
+ :js,
37
+ :layout,
38
+ :plain,
39
+ :raw,
40
+ :template,
41
+ :text,
42
+ :xml
43
+ ]
44
+
45
+ def_node_matcher :non_content_status?, <<~PATTERN
46
+ (pair
47
+ (sym :status)
48
+ {(sym NON_CONTENT_STATUSES) (int NON_CONTENT_STATUS_CODES)}
49
+ )
50
+ PATTERN
51
+
52
+ def_node_matcher :unused_render_content?, <<~PATTERN
53
+ (send nil? :render {
54
+ (hash <#non_content_status? $(pair (sym BODY_OPTIONS) _) ...>) |
55
+ $({str sym} _) (hash <#non_content_status? ...>)
56
+ })
57
+ PATTERN
58
+
59
+ def on_send(node)
60
+ unused_render_content?(node) do |unused_content_node|
61
+ add_offense(unused_content_node)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -39,7 +39,6 @@ module RuboCop
39
39
  # # bad
40
40
  # User.exists?(name: 'john')
41
41
  # User.exists?(['name = ?', 'john'])
42
- # User.exists?('name = ?', 'john')
43
42
  # user.posts.exists?(published: true)
44
43
  #
45
44
  # # good
@@ -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'
@@ -32,6 +33,7 @@ require_relative 'rails/bulk_change_table'
32
33
  require_relative 'rails/compact_blank'
33
34
  require_relative 'rails/content_tag'
34
35
  require_relative 'rails/create_table_with_timestamps'
36
+ require_relative 'rails/dangerous_column_names'
35
37
  require_relative 'rails/date'
36
38
  require_relative 'rails/default_scope'
37
39
  require_relative 'rails/delegate'
@@ -45,6 +47,7 @@ require_relative 'rails/dynamic_find_by'
45
47
  require_relative 'rails/eager_evaluation_log_message'
46
48
  require_relative 'rails/enum_hash'
47
49
  require_relative 'rails/enum_uniqueness'
50
+ require_relative 'rails/env_local'
48
51
  require_relative 'rails/environment_comparison'
49
52
  require_relative 'rails/environment_variable_access'
50
53
  require_relative 'rails/exit'
@@ -87,6 +90,7 @@ require_relative 'rails/presence'
87
90
  require_relative 'rails/present'
88
91
  require_relative 'rails/rake_environment'
89
92
  require_relative 'rails/read_write_attribute'
93
+ require_relative 'rails/redundant_active_record_all_method'
90
94
  require_relative 'rails/redundant_allow_nil'
91
95
  require_relative 'rails/redundant_foreign_key'
92
96
  require_relative 'rails/redundant_presence_validation_on_belongs_to'
@@ -110,6 +114,7 @@ require_relative 'rails/safe_navigation_with_blank'
110
114
  require_relative 'rails/save_bang'
111
115
  require_relative 'rails/schema_comment'
112
116
  require_relative 'rails/scope_args'
117
+ require_relative 'rails/select_map'
113
118
  require_relative 'rails/short_i18n'
114
119
  require_relative 'rails/skips_model_validations'
115
120
  require_relative 'rails/squished_sql_heredocs'
@@ -126,6 +131,7 @@ require_relative 'rails/uniq_before_pluck'
126
131
  require_relative 'rails/unique_validation_without_index'
127
132
  require_relative 'rails/unknown_env'
128
133
  require_relative 'rails/unused_ignored_columns'
134
+ require_relative 'rails/unused_render_content'
129
135
  require_relative 'rails/validation'
130
136
  require_relative 'rails/where_equals'
131
137
  require_relative 'rails/where_exists'
@@ -43,7 +43,7 @@ module RuboCop
43
43
  return unless path
44
44
 
45
45
  ast = parse(path, target_ruby_version)
46
- Schema.new(ast)
46
+ Schema.new(ast) if ast
47
47
  end
48
48
 
49
49
  def parse(path, target_ruby_version)
@@ -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.20.2'
7
+ STRING = '2.22.1'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
data/lib/rubocop-rails.rb CHANGED
@@ -17,6 +17,14 @@ require_relative 'rubocop/cop/rails_cops'
17
17
 
18
18
  RuboCop::Cop::Style::HashExcept.minimum_target_ruby_version(2.0)
19
19
 
20
+ RuboCop::Cop::Style::InverseMethods.singleton_class.prepend(
21
+ Module.new do
22
+ def autocorrect_incompatible_with
23
+ super.push(RuboCop::Cop::Rails::NegateInclude)
24
+ end
25
+ end
26
+ )
27
+
20
28
  RuboCop::Cop::Style::MethodCallWithArgsParentheses.singleton_class.prepend(
21
29
  Module.new do
22
30
  def autocorrect_incompatible_with
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.20.2
4
+ version: 2.22.1
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-06-19 00:00:00.000000000 Z
13
+ date: 2023-10-28 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
@@ -106,6 +107,7 @@ files:
106
107
  - lib/rubocop/cop/rails/compact_blank.rb
107
108
  - lib/rubocop/cop/rails/content_tag.rb
108
109
  - lib/rubocop/cop/rails/create_table_with_timestamps.rb
110
+ - lib/rubocop/cop/rails/dangerous_column_names.rb
109
111
  - lib/rubocop/cop/rails/date.rb
110
112
  - lib/rubocop/cop/rails/default_scope.rb
111
113
  - lib/rubocop/cop/rails/delegate.rb
@@ -119,6 +121,7 @@ files:
119
121
  - lib/rubocop/cop/rails/eager_evaluation_log_message.rb
120
122
  - lib/rubocop/cop/rails/enum_hash.rb
121
123
  - lib/rubocop/cop/rails/enum_uniqueness.rb
124
+ - lib/rubocop/cop/rails/env_local.rb
122
125
  - lib/rubocop/cop/rails/environment_comparison.rb
123
126
  - lib/rubocop/cop/rails/environment_variable_access.rb
124
127
  - lib/rubocop/cop/rails/exit.rb
@@ -161,6 +164,7 @@ files:
161
164
  - lib/rubocop/cop/rails/present.rb
162
165
  - lib/rubocop/cop/rails/rake_environment.rb
163
166
  - lib/rubocop/cop/rails/read_write_attribute.rb
167
+ - lib/rubocop/cop/rails/redundant_active_record_all_method.rb
164
168
  - lib/rubocop/cop/rails/redundant_allow_nil.rb
165
169
  - lib/rubocop/cop/rails/redundant_foreign_key.rb
166
170
  - lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb
@@ -184,6 +188,7 @@ files:
184
188
  - lib/rubocop/cop/rails/save_bang.rb
185
189
  - lib/rubocop/cop/rails/schema_comment.rb
186
190
  - lib/rubocop/cop/rails/scope_args.rb
191
+ - lib/rubocop/cop/rails/select_map.rb
187
192
  - lib/rubocop/cop/rails/short_i18n.rb
188
193
  - lib/rubocop/cop/rails/skips_model_validations.rb
189
194
  - lib/rubocop/cop/rails/squished_sql_heredocs.rb
@@ -200,6 +205,7 @@ files:
200
205
  - lib/rubocop/cop/rails/unique_validation_without_index.rb
201
206
  - lib/rubocop/cop/rails/unknown_env.rb
202
207
  - lib/rubocop/cop/rails/unused_ignored_columns.rb
208
+ - lib/rubocop/cop/rails/unused_render_content.rb
203
209
  - lib/rubocop/cop/rails/validation.rb
204
210
  - lib/rubocop/cop/rails/where_equals.rb
205
211
  - lib/rubocop/cop/rails/where_exists.rb
@@ -219,7 +225,7 @@ metadata:
219
225
  homepage_uri: https://docs.rubocop.org/rubocop-rails/
220
226
  changelog_uri: https://github.com/rubocop/rubocop-rails/blob/master/CHANGELOG.md
221
227
  source_code_uri: https://github.com/rubocop/rubocop-rails/
222
- documentation_uri: https://docs.rubocop.org/rubocop-rails/2.20/
228
+ documentation_uri: https://docs.rubocop.org/rubocop-rails/2.22/
223
229
  bug_tracker_uri: https://github.com/rubocop/rubocop-rails/issues
224
230
  rubygems_mfa_required: 'true'
225
231
  post_install_message: