rubocop-rails 2.20.2 → 2.22.1

Sign up to get free protection for your applications and to get access to all the features.
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: