rubocop-rails 2.20.2 → 2.24.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -7
  3. data/config/default.yml +72 -10
  4. data/lib/rubocop/cop/mixin/active_record_helper.rb +15 -3
  5. data/lib/rubocop/cop/mixin/database_type_resolvable.rb +66 -0
  6. data/lib/rubocop/cop/mixin/index_method.rb +2 -2
  7. data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +3 -1
  8. data/lib/rubocop/cop/rails/action_controller_test_case.rb +2 -2
  9. data/lib/rubocop/cop/rails/action_filter.rb +3 -0
  10. data/lib/rubocop/cop/rails/active_record_aliases.rb +2 -2
  11. data/lib/rubocop/cop/rails/active_support_aliases.rb +6 -5
  12. data/lib/rubocop/cop/rails/active_support_on_load.rb +21 -1
  13. data/lib/rubocop/cop/rails/after_commit_override.rb +1 -1
  14. data/lib/rubocop/cop/rails/bulk_change_table.rb +8 -41
  15. data/lib/rubocop/cop/rails/content_tag.rb +1 -1
  16. data/lib/rubocop/cop/rails/dangerous_column_names.rb +446 -0
  17. data/lib/rubocop/cop/rails/date.rb +1 -1
  18. data/lib/rubocop/cop/rails/duplicate_association.rb +69 -12
  19. data/lib/rubocop/cop/rails/dynamic_find_by.rb +3 -3
  20. data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +2 -2
  21. data/lib/rubocop/cop/rails/env_local.rb +46 -0
  22. data/lib/rubocop/cop/rails/expanded_date_range.rb +1 -1
  23. data/lib/rubocop/cop/rails/file_path.rb +9 -6
  24. data/lib/rubocop/cop/rails/find_by.rb +3 -3
  25. data/lib/rubocop/cop/rails/find_by_id.rb +9 -23
  26. data/lib/rubocop/cop/rails/freeze_time.rb +1 -1
  27. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +1 -1
  28. data/lib/rubocop/cop/rails/helper_instance_variable.rb +1 -1
  29. data/lib/rubocop/cop/rails/http_status.rb +4 -3
  30. data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +63 -13
  31. data/lib/rubocop/cop/rails/inquiry.rb +1 -0
  32. data/lib/rubocop/cop/rails/inverse_of.rb +1 -1
  33. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +7 -8
  34. data/lib/rubocop/cop/rails/not_null_column.rb +13 -3
  35. data/lib/rubocop/cop/rails/output.rb +3 -2
  36. data/lib/rubocop/cop/rails/pick.rb +6 -5
  37. data/lib/rubocop/cop/rails/pluck.rb +1 -1
  38. data/lib/rubocop/cop/rails/pluck_id.rb +2 -1
  39. data/lib/rubocop/cop/rails/pluck_in_where.rb +18 -5
  40. data/lib/rubocop/cop/rails/rake_environment.rb +22 -6
  41. data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +219 -0
  42. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +7 -0
  43. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +1 -1
  44. data/lib/rubocop/cop/rails/response_parsed_body.rb +52 -10
  45. data/lib/rubocop/cop/rails/reversible_migration.rb +4 -4
  46. data/lib/rubocop/cop/rails/root_pathname_methods.rb +38 -4
  47. data/lib/rubocop/cop/rails/save_bang.rb +15 -8
  48. data/lib/rubocop/cop/rails/schema_comment.rb +16 -10
  49. data/lib/rubocop/cop/rails/select_map.rb +78 -0
  50. data/lib/rubocop/cop/rails/time_zone.rb +13 -5
  51. data/lib/rubocop/cop/rails/transaction_exit_statement.rb +29 -10
  52. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +12 -4
  53. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +2 -2
  54. data/lib/rubocop/cop/rails/unknown_env.rb +5 -1
  55. data/lib/rubocop/cop/rails/unused_render_content.rb +67 -0
  56. data/lib/rubocop/cop/rails/validation.rb +2 -2
  57. data/lib/rubocop/cop/rails/where_equals.rb +3 -2
  58. data/lib/rubocop/cop/rails/where_exists.rb +9 -9
  59. data/lib/rubocop/cop/rails/where_missing.rb +6 -2
  60. data/lib/rubocop/cop/rails/where_not.rb +8 -6
  61. data/lib/rubocop/cop/rails_cops.rb +6 -0
  62. data/lib/rubocop/rails/schema_loader/schema.rb +3 -2
  63. data/lib/rubocop/rails/schema_loader.rb +5 -15
  64. data/lib/rubocop/rails/version.rb +1 -1
  65. data/lib/rubocop-rails.rb +8 -0
  66. metadata +30 -4
@@ -163,9 +163,9 @@ module RuboCop
163
163
 
164
164
  def autocorrect_extension_after_rails_root_join_in_dstr(corrector, node, rails_root_index, extension_node)
165
165
  rails_root_node = node.children[rails_root_index].children.first
166
- return unless rails_root_node.arguments.last.str_type?
166
+ return unless rails_root_node.last_argument.str_type?
167
167
 
168
- corrector.insert_before(rails_root_node.arguments.last.location.end, extension_node.source)
168
+ corrector.insert_before(rails_root_node.last_argument.location.end, extension_node.source)
169
169
  corrector.remove(extension_node)
170
170
  end
171
171
 
@@ -174,17 +174,20 @@ module RuboCop
174
174
  corrector.remove(
175
175
  range_with_surrounding_space(
176
176
  range_with_surrounding_comma(
177
- node.arguments.first.source_range,
177
+ node.first_argument.source_range,
178
178
  :right
179
179
  ),
180
180
  side: :right
181
181
  )
182
182
  )
183
+ node.arguments.filter(&:str_type?).each do |argument|
184
+ corrector.replace(argument, argument.value.delete_prefix('/').inspect)
185
+ end
183
186
  corrector.insert_after(node, '.to_s')
184
187
  end
185
188
 
186
189
  def autocorrect_rails_root_join_with_string_arguments(corrector, node)
187
- corrector.replace(node.arguments.first, %("#{node.arguments.map(&:value).join('/')}"))
190
+ corrector.replace(node.first_argument, %("#{node.arguments.map(&:value).join('/')}"))
188
191
  node.arguments[1..].each do |argument|
189
192
  corrector.remove(
190
193
  range_with_surrounding_comma(
@@ -218,7 +221,7 @@ module RuboCop
218
221
  end
219
222
 
220
223
  def append_argument(corrector, node, argument_source)
221
- corrector.insert_after(node.arguments.last, %(, "#{argument_source}"))
224
+ corrector.insert_after(node.last_argument, %(, "#{argument_source}"))
222
225
  end
223
226
 
224
227
  def replace_with_rails_root_join(corrector, node, argument_source)
@@ -230,7 +233,7 @@ module RuboCop
230
233
  end
231
234
 
232
235
  def extension_node?(node)
233
- node&.str_type? && node.source.start_with?('.')
236
+ node&.str_type? && node.source.match?(/\A\.[A-Za-z]+/)
234
237
  end
235
238
  end
236
239
  end
@@ -28,7 +28,7 @@ module RuboCop
28
28
  include RangeHelp
29
29
  extend AutoCorrector
30
30
 
31
- MSG = 'Use `find_by` instead of `where.%<method>s`.'
31
+ MSG = 'Use `find_by` instead of `where%<dot>s%<method>s`.'
32
32
  RESTRICT_ON_SEND = %i[first take].freeze
33
33
 
34
34
  def on_send(node)
@@ -37,7 +37,7 @@ module RuboCop
37
37
 
38
38
  range = offense_range(node)
39
39
 
40
- add_offense(range, message: format(MSG, method: node.method_name)) do |corrector|
40
+ add_offense(range, message: format(MSG, dot: node.loc.dot.source, method: node.method_name)) do |corrector|
41
41
  autocorrect(corrector, node)
42
42
  end
43
43
  end
@@ -59,7 +59,7 @@ module RuboCop
59
59
  return if node.method?(:first)
60
60
 
61
61
  where_loc = node.receiver.loc.selector
62
- first_loc = range_between(node.loc.dot.begin_pos, node.loc.selector.end_pos)
62
+ first_loc = range_between(node.receiver.source_range.end_pos, node.loc.selector.end_pos)
63
63
 
64
64
  corrector.replace(where_loc, 'find_by')
65
65
  corrector.replace(first_loc, '')
@@ -24,40 +24,39 @@ module RuboCop
24
24
  RESTRICT_ON_SEND = %i[take! find_by_id! find_by!].freeze
25
25
 
26
26
  def_node_matcher :where_take?, <<~PATTERN
27
- (send
28
- $(send _ :where
27
+ (call
28
+ $(call _ :where
29
29
  (hash
30
30
  (pair (sym :id) $_))) :take!)
31
31
  PATTERN
32
32
 
33
33
  def_node_matcher :find_by?, <<~PATTERN
34
34
  {
35
- (send _ :find_by_id! $_)
36
- (send _ :find_by! (hash (pair (sym :id) $_)))
35
+ (call _ :find_by_id! $_)
36
+ (call _ :find_by! (hash (pair (sym :id) $_)))
37
37
  }
38
38
  PATTERN
39
39
 
40
40
  def on_send(node)
41
41
  where_take?(node) do |where, id_value|
42
42
  range = where_take_offense_range(node, where)
43
- bad_method = build_where_take_bad_method(id_value)
44
43
 
45
- register_offense(range, id_value, bad_method)
44
+ register_offense(range, id_value)
46
45
  end
47
46
 
48
47
  find_by?(node) do |id_value|
49
48
  range = find_by_offense_range(node)
50
- bad_method = build_find_by_bad_method(node, id_value)
51
49
 
52
- register_offense(range, id_value, bad_method)
50
+ register_offense(range, id_value)
53
51
  end
54
52
  end
53
+ alias on_csend on_send
55
54
 
56
55
  private
57
56
 
58
- def register_offense(range, id_value, bad_method)
57
+ def register_offense(range, id_value)
59
58
  good_method = build_good_method(id_value)
60
- message = format(MSG, good_method: good_method, bad_method: bad_method)
59
+ message = format(MSG, good_method: good_method, bad_method: range.source)
61
60
 
62
61
  add_offense(range, message: message) do |corrector|
63
62
  corrector.replace(range, good_method)
@@ -75,19 +74,6 @@ module RuboCop
75
74
  def build_good_method(id_value)
76
75
  "find(#{id_value.source})"
77
76
  end
78
-
79
- def build_where_take_bad_method(id_value)
80
- "where(id: #{id_value.source}).take!"
81
- end
82
-
83
- def build_find_by_bad_method(node, id_value)
84
- case node.method_name
85
- when :find_by_id!
86
- "find_by_id!(#{id_value.source})"
87
- when :find_by!
88
- "find_by!(id: #{id_value.source})"
89
- end
90
- end
91
77
  end
92
78
  end
93
79
  end
@@ -69,7 +69,7 @@ module RuboCop
69
69
  return false unless CONVERT_METHODS.include?(method_name)
70
70
 
71
71
  child_node, child_method_name, time_argument = *node.children
72
- return if time_argument
72
+ return false if time_argument
73
73
 
74
74
  current_time?(child_node, child_method_name)
75
75
  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
@@ -8,6 +8,7 @@ module RuboCop
8
8
  # @example EnforcedStyle: symbolic (default)
9
9
  # # bad
10
10
  # render :foo, status: 200
11
+ # render :foo, status: '200'
11
12
  # render json: { foo: 'bar' }, status: 200
12
13
  # render plain: 'foo/bar', status: 304
13
14
  # redirect_to root_url, status: 301
@@ -50,7 +51,7 @@ module RuboCop
50
51
  PATTERN
51
52
 
52
53
  def_node_matcher :status_code, <<~PATTERN
53
- (hash <(pair (sym :status) ${int sym}) ...>)
54
+ (hash <(pair (sym :status) ${int sym str}) ...>)
54
55
  PATTERN
55
56
 
56
57
  def on_send(node)
@@ -108,7 +109,7 @@ module RuboCop
108
109
  private
109
110
 
110
111
  def symbol
111
- ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number)
112
+ ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number.to_i)
112
113
  end
113
114
 
114
115
  def number
@@ -133,7 +134,7 @@ module RuboCop
133
134
  end
134
135
 
135
136
  def offensive?
136
- !node.int_type? && !permitted_symbol?
137
+ !node.int_type? && !permitted_symbol? && number
137
138
  end
138
139
 
139
140
  def message
@@ -5,7 +5,13 @@ module RuboCop
5
5
  module Rails
6
6
  # Checks for places where I18n "lazy" lookup can be used.
7
7
  #
8
- # @example
8
+ # This cop has two different enforcement modes. When the EnforcedStyle
9
+ # is `lazy` (the default), explicit lookups are added as offenses.
10
+ #
11
+ # When the EnforcedStyle is `explicit` then lazy lookups are added as
12
+ # offenses.
13
+ #
14
+ # @example EnforcedStyle: lazy (default)
9
15
  # # en.yml
10
16
  # # en:
11
17
  # # books:
@@ -28,11 +34,29 @@ module RuboCop
28
34
  # end
29
35
  # end
30
36
  #
37
+ # @example EnforcedStyle: explicit
38
+ # # bad
39
+ # class BooksController < ApplicationController
40
+ # def create
41
+ # # ...
42
+ # redirect_to books_url, notice: t('.success')
43
+ # end
44
+ # end
45
+ #
46
+ # # good
47
+ # class BooksController < ApplicationController
48
+ # def create
49
+ # # ...
50
+ # redirect_to books_url, notice: t('books.create.success')
51
+ # end
52
+ # end
53
+ #
31
54
  class I18nLazyLookup < Base
55
+ include ConfigurableEnforcedStyle
32
56
  include VisibilityHelp
33
57
  extend AutoCorrector
34
58
 
35
- MSG = 'Use "lazy" lookup for the text used in controllers.'
59
+ MSG = 'Use %<style>s lookup for the text used in controllers.'
36
60
 
37
61
  RESTRICT_ON_SEND = %i[translate t].freeze
38
62
 
@@ -42,23 +66,45 @@ module RuboCop
42
66
 
43
67
  def on_send(node)
44
68
  translate_call?(node) do |key_node|
45
- key = key_node.value
46
- return if key.to_s.start_with?('.')
69
+ case style
70
+ when :lazy
71
+ handle_lazy_style(node, key_node)
72
+ when :explicit
73
+ handle_explicit_style(node, key_node)
74
+ end
75
+ end
76
+ end
77
+
78
+ private
47
79
 
48
- controller, action = controller_and_action(node)
49
- return unless controller && action
80
+ def handle_lazy_style(node, key_node)
81
+ key = key_node.value
82
+ return if key.to_s.start_with?('.')
50
83
 
51
- scoped_key = get_scoped_key(key_node, controller, action)
52
- return unless key == scoped_key
84
+ controller, action = controller_and_action(node)
85
+ return unless controller && action
53
86
 
54
- add_offense(key_node) do |corrector|
55
- unscoped_key = key_node.value.to_s.split('.').last
56
- corrector.replace(key_node, "'.#{unscoped_key}'")
57
- end
87
+ scoped_key = get_scoped_key(key_node, controller, action)
88
+ return unless key == scoped_key
89
+
90
+ add_offense(key_node) do |corrector|
91
+ unscoped_key = key_node.value.to_s.split('.').last
92
+ corrector.replace(key_node, "'.#{unscoped_key}'")
58
93
  end
59
94
  end
60
95
 
61
- private
96
+ def handle_explicit_style(node, key_node)
97
+ key = key_node.value
98
+ return unless key.to_s.start_with?('.')
99
+
100
+ controller, action = controller_and_action(node)
101
+ return unless controller && action
102
+
103
+ scoped_key = get_scoped_key(key_node, controller, action)
104
+ add_offense(key_node) do |corrector|
105
+ corrector.replace(key_node, "'#{scoped_key}'")
106
+ end
107
+ end
62
108
 
63
109
  def controller_and_action(node)
64
110
  action_node = node.each_ancestor(:def).first
@@ -90,6 +136,10 @@ module RuboCop
90
136
 
91
137
  path.delete_suffix('Controller').underscore
92
138
  end
139
+
140
+ def message(_range)
141
+ format(MSG, style: style)
142
+ end
93
143
  end
94
144
  end
95
145
  end
@@ -33,6 +33,7 @@ module RuboCop
33
33
 
34
34
  add_offense(node.loc.selector)
35
35
  end
36
+ alias on_csend on_send
36
37
  end
37
38
  end
38
39
  end
@@ -222,7 +222,7 @@ module RuboCop
222
222
 
223
223
  def with_options_arguments(recv, node)
224
224
  blocks = node.each_ancestor(:block).select do |block|
225
- block.send_node.command?(:with_options) && same_context_in_with_options?(block.arguments.first, recv)
225
+ block.send_node.command?(:with_options) && same_context_in_with_options?(block.first_argument, recv)
226
226
  end
227
227
  blocks.flat_map { |n| n.send_node.arguments }
228
228
  end
@@ -122,24 +122,23 @@ module RuboCop
122
122
  parent = node.each_ancestor(:class, :module).first
123
123
  return unless parent
124
124
 
125
+ # NOTE: a `:begin` node may not exist if the class/module consists of a single statement
125
126
  block = parent.each_child_node(:begin).first
126
- return unless block
127
-
128
127
  defined_action_methods = defined_action_methods(block)
129
128
 
130
- methods = array_values(methods_node).reject do |method|
131
- defined_action_methods.include?(method)
132
- end
129
+ unmatched_methods = array_values(methods_node) - defined_action_methods
130
+ return if unmatched_methods.empty?
133
131
 
134
- message = message(methods, parent)
135
- add_offense(node, message: message) unless methods.empty?
132
+ message = message(unmatched_methods, parent)
133
+ add_offense(node, message: message)
136
134
  end
137
135
 
138
136
  private
139
137
 
140
138
  def defined_action_methods(block)
141
- defined_methods = block.each_child_node(:def).map(&:method_name)
139
+ return [] unless block
142
140
 
141
+ defined_methods = block.each_child_node(:def).map(&:method_name)
143
142
  defined_methods + aliased_action_methods(block, defined_methods)
144
143
  end
145
144
 
@@ -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
 
@@ -28,13 +28,13 @@ module RuboCop
28
28
  extend AutoCorrector
29
29
  extend TargetRailsVersion
30
30
 
31
- MSG = 'Prefer `pick(%<args>s)` over `pluck(%<args>s).first`.'
31
+ MSG = 'Prefer `pick(%<args>s)` over `%<current>s`.'
32
32
  RESTRICT_ON_SEND = %i[first].freeze
33
33
 
34
34
  minimum_target_rails_version 6.0
35
35
 
36
36
  def_node_matcher :pick_candidate?, <<~PATTERN
37
- (send (send _ :pluck ...) :first)
37
+ (call (call _ :pluck ...) :first)
38
38
  PATTERN
39
39
 
40
40
  def on_send(node)
@@ -44,7 +44,7 @@ module RuboCop
44
44
  node_selector = node.loc.selector
45
45
  range = receiver_selector.join(node_selector)
46
46
 
47
- add_offense(range, message: message(receiver)) do |corrector|
47
+ add_offense(range, message: message(receiver, range)) do |corrector|
48
48
  first_range = receiver.source_range.end.join(node_selector)
49
49
 
50
50
  corrector.remove(first_range)
@@ -52,11 +52,12 @@ module RuboCop
52
52
  end
53
53
  end
54
54
  end
55
+ alias on_csend on_send
55
56
 
56
57
  private
57
58
 
58
- def message(receiver)
59
- format(MSG, args: receiver.arguments.map(&:source).join(', '))
59
+ def message(receiver, current)
60
+ format(MSG, args: receiver.arguments.map(&:source).join(', '), current: current.source)
60
61
  end
61
62
  end
62
63
  end
@@ -38,7 +38,7 @@ module RuboCop
38
38
  minimum_target_rails_version 5.0
39
39
 
40
40
  def_node_matcher :pluck_candidate?, <<~PATTERN
41
- ({block numblock} (send _ {:map :collect}) $_argument (send lvar :[] $_key))
41
+ ({block numblock} (call _ {:map :collect}) $_argument (send lvar :[] $_key))
42
42
  PATTERN
43
43
 
44
44
  def on_block(node)
@@ -34,7 +34,7 @@ module RuboCop
34
34
  RESTRICT_ON_SEND = %i[pluck].freeze
35
35
 
36
36
  def_node_matcher :pluck_id_call?, <<~PATTERN
37
- (send _ :pluck {(sym :id) (send nil? :primary_key)})
37
+ (call _ :pluck {(sym :id) (send nil? :primary_key)})
38
38
  PATTERN
39
39
 
40
40
  def on_send(node)
@@ -47,6 +47,7 @@ module RuboCop
47
47
  corrector.replace(offense_range(node), 'ids')
48
48
  end
49
49
  end
50
+ alias on_csend on_send
50
51
 
51
52
  private
52
53
 
@@ -22,10 +22,13 @@ module RuboCop
22
22
  # @example
23
23
  # # bad
24
24
  # Post.where(user_id: User.active.pluck(:id))
25
+ # Post.where(user_id: User.active.ids)
26
+ # Post.where.not(user_id: User.active.pluck(:id))
25
27
  #
26
28
  # # good
27
29
  # Post.where(user_id: User.active.select(:id))
28
30
  # Post.where(user_id: active_users.select(:id))
31
+ # Post.where.not(user_id: active_users.select(:id))
29
32
  #
30
33
  # @example EnforcedStyle: conservative (default)
31
34
  # # good
@@ -40,8 +43,9 @@ module RuboCop
40
43
  include ConfigurableEnforcedStyle
41
44
  extend AutoCorrector
42
45
 
43
- MSG = 'Use `select` instead of `pluck` within `where` query method.'
44
- RESTRICT_ON_SEND = %i[pluck].freeze
46
+ MSG_SELECT = 'Use `select` instead of `pluck` within `where` query method.'
47
+ MSG_IDS = 'Use `select(:id)` instead of `ids` within `where` query method.'
48
+ RESTRICT_ON_SEND = %i[pluck ids].freeze
45
49
 
46
50
  def on_send(node)
47
51
  return unless in_where?(node)
@@ -49,17 +53,26 @@ module RuboCop
49
53
 
50
54
  range = node.loc.selector
51
55
 
52
- add_offense(range) do |corrector|
53
- corrector.replace(range, 'select')
56
+ if node.method?(:ids)
57
+ replacement = 'select(:id)'
58
+ message = MSG_IDS
59
+ else
60
+ replacement = 'select'
61
+ message = MSG_SELECT
62
+ end
63
+
64
+ add_offense(range, message: message) do |corrector|
65
+ corrector.replace(range, replacement)
54
66
  end
55
67
  end
68
+ alias on_csend on_send
56
69
 
57
70
  private
58
71
 
59
72
  def root_receiver(node)
60
73
  receiver = node.receiver
61
74
 
62
- if receiver&.send_type?
75
+ if receiver&.call_type?
63
76
  root_receiver(receiver)
64
77
  else
65
78
  receiver
@@ -45,16 +45,24 @@ module RuboCop
45
45
  return if with_dependencies?(task_method)
46
46
 
47
47
  add_offense(task_method) do |corrector|
48
- task_name = task_method.arguments[0]
49
- task_dependency = correct_task_dependency(task_name)
50
-
51
- corrector.replace(task_name, task_dependency)
48
+ if with_arguments?(task_method)
49
+ new_task_dependency = correct_task_arguments_dependency(task_method)
50
+ corrector.replace(task_arguments(task_method), new_task_dependency)
51
+ else
52
+ task_name = task_method.first_argument
53
+ new_task_dependency = correct_task_dependency(task_name)
54
+ corrector.replace(task_name, new_task_dependency)
55
+ end
52
56
  end
53
57
  end
54
58
  end
55
59
 
56
60
  private
57
61
 
62
+ def correct_task_arguments_dependency(task_method)
63
+ "#{task_arguments(task_method).source} => :environment"
64
+ end
65
+
58
66
  def correct_task_dependency(task_name)
59
67
  if task_name.sym_type?
60
68
  "#{task_name.source.delete(':|\'|"')}: :environment"
@@ -64,7 +72,7 @@ module RuboCop
64
72
  end
65
73
 
66
74
  def task_name(node)
67
- first_arg = node.arguments[0]
75
+ first_arg = node.first_argument
68
76
  case first_arg&.type
69
77
  when :sym, :str
70
78
  first_arg.value.to_sym
@@ -80,8 +88,16 @@ module RuboCop
80
88
  end
81
89
  end
82
90
 
91
+ def task_arguments(node)
92
+ node.arguments[1]
93
+ end
94
+
95
+ def with_arguments?(node)
96
+ node.arguments.size > 1 && node.arguments[1].array_type?
97
+ end
98
+
83
99
  def with_dependencies?(node)
84
- first_arg = node.arguments[0]
100
+ first_arg = node.first_argument
85
101
  return false unless first_arg
86
102
 
87
103
  if first_arg.hash_type?