rubocop-rails 2.20.2 → 2.24.1

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