rubocop-rails 2.25.1 → 2.32.0

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +60 -8
  4. data/config/default.yml +103 -51
  5. data/lib/rubocop/cop/mixin/active_record_helper.rb +2 -2
  6. data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +2 -2
  7. data/lib/rubocop/cop/mixin/database_type_resolvable.rb +2 -2
  8. data/lib/rubocop/cop/mixin/enforce_superclass.rb +6 -1
  9. data/lib/rubocop/cop/mixin/index_method.rb +69 -61
  10. data/lib/rubocop/cop/mixin/routes_helper.rb +20 -0
  11. data/lib/rubocop/cop/mixin/target_rails_version.rb +3 -5
  12. data/lib/rubocop/cop/rails/action_order.rb +1 -5
  13. data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +1 -5
  14. data/lib/rubocop/cop/rails/add_column_index.rb +1 -0
  15. data/lib/rubocop/cop/rails/application_record.rb +4 -0
  16. data/lib/rubocop/cop/rails/arel_star.rb +5 -5
  17. data/lib/rubocop/cop/rails/belongs_to.rb +1 -1
  18. data/lib/rubocop/cop/rails/blank.rb +1 -1
  19. data/lib/rubocop/cop/rails/bulk_change_table.rb +3 -2
  20. data/lib/rubocop/cop/rails/compact_blank.rb +29 -8
  21. data/lib/rubocop/cop/rails/content_tag.rb +1 -1
  22. data/lib/rubocop/cop/rails/dangerous_column_names.rb +2 -0
  23. data/lib/rubocop/cop/rails/date.rb +2 -2
  24. data/lib/rubocop/cop/rails/delegate.rb +53 -7
  25. data/lib/rubocop/cop/rails/duplicate_association.rb +8 -4
  26. data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +1 -3
  27. data/lib/rubocop/cop/rails/enum_hash.rb +31 -8
  28. data/lib/rubocop/cop/rails/enum_syntax.rb +130 -0
  29. data/lib/rubocop/cop/rails/enum_uniqueness.rb +29 -7
  30. data/lib/rubocop/cop/rails/env_local.rb +26 -3
  31. data/lib/rubocop/cop/rails/file_path.rb +62 -10
  32. data/lib/rubocop/cop/rails/http_positional_arguments.rb +7 -0
  33. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +1 -1
  34. data/lib/rubocop/cop/rails/index_by.rb +37 -12
  35. data/lib/rubocop/cop/rails/index_with.rb +37 -12
  36. data/lib/rubocop/cop/rails/inquiry.rb +1 -1
  37. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +11 -1
  38. data/lib/rubocop/cop/rails/match_route.rb +1 -9
  39. data/lib/rubocop/cop/rails/multiple_route_paths.rb +50 -0
  40. data/lib/rubocop/cop/rails/not_null_column.rb +6 -2
  41. data/lib/rubocop/cop/rails/output.rb +1 -2
  42. data/lib/rubocop/cop/rails/pluck.rb +30 -4
  43. data/lib/rubocop/cop/rails/pluck_in_where.rb +17 -8
  44. data/lib/rubocop/cop/rails/pluralization_grammar.rb +30 -16
  45. data/lib/rubocop/cop/rails/presence.rb +1 -1
  46. data/lib/rubocop/cop/rails/present.rb +1 -3
  47. data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +1 -30
  48. data/lib/rubocop/cop/rails/redundant_foreign_key.rb +1 -1
  49. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +9 -0
  50. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +7 -2
  51. data/lib/rubocop/cop/rails/reflection_class_name.rb +3 -3
  52. data/lib/rubocop/cop/rails/relative_date_constant.rb +1 -1
  53. data/lib/rubocop/cop/rails/render_plain_text.rb +6 -3
  54. data/lib/rubocop/cop/rails/request_referer.rb +1 -1
  55. data/lib/rubocop/cop/rails/reversible_migration.rb +4 -1
  56. data/lib/rubocop/cop/rails/root_pathname_methods.rb +21 -12
  57. data/lib/rubocop/cop/rails/save_bang.rb +8 -7
  58. data/lib/rubocop/cop/rails/schema_comment.rb +2 -1
  59. data/lib/rubocop/cop/rails/select_map.rb +3 -2
  60. data/lib/rubocop/cop/rails/skips_model_validations.rb +5 -3
  61. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +1 -1
  62. data/lib/rubocop/cop/rails/strip_heredoc.rb +1 -1
  63. data/lib/rubocop/cop/rails/strong_parameters_expect.rb +104 -0
  64. data/lib/rubocop/cop/rails/three_state_boolean_column.rb +3 -2
  65. data/lib/rubocop/cop/rails/time_zone.rb +16 -7
  66. data/lib/rubocop/cop/rails/transaction_exit_statement.rb +7 -2
  67. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +10 -33
  68. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +1 -1
  69. data/lib/rubocop/cop/rails/validation.rb +1 -1
  70. data/lib/rubocop/cop/rails/where_equals.rb +28 -12
  71. data/lib/rubocop/cop/rails/where_not.rb +11 -6
  72. data/lib/rubocop/cop/rails/where_range.rb +7 -2
  73. data/lib/rubocop/cop/rails_cops.rb +4 -0
  74. data/lib/rubocop/rails/migration_file_skippable.rb +54 -0
  75. data/lib/rubocop/rails/plugin.rb +48 -0
  76. data/lib/rubocop/rails/version.rb +1 -1
  77. data/lib/rubocop/rails.rb +1 -8
  78. data/lib/rubocop-rails.rb +4 -5
  79. metadata +29 -12
  80. data/lib/rubocop/rails/inject.rb +0 -18
@@ -41,10 +41,14 @@ module RuboCop
41
41
  # change_column_null :products, :category_id, false
42
42
  class NotNullColumn < Base
43
43
  include DatabaseTypeResolvable
44
+ include MigrationsHelper
44
45
 
45
46
  MSG = 'Do not add a NOT NULL column without a default value.'
46
47
  RESTRICT_ON_SEND = %i[add_column add_reference].freeze
47
48
 
49
+ VIRTUAL_TYPE_VALUES = [:virtual, 'virtual'].freeze
50
+ TEXT_TYPE_VALUES = [:text, 'text'].freeze
51
+
48
52
  def_node_matcher :add_not_null_column?, <<~PATTERN
49
53
  (send nil? :add_column _ _ $_ (hash $...))
50
54
  PATTERN
@@ -91,8 +95,8 @@ module RuboCop
91
95
 
92
96
  def check_column(type, pairs)
93
97
  if type.respond_to?(:value)
94
- return if type.value == :virtual || type.value == 'virtual'
95
- return if (type.value == :text || type.value == 'text') && database == MYSQL
98
+ return if VIRTUAL_TYPE_VALUES.include?(type.value)
99
+ return if TEXT_TYPE_VALUES.include?(type.value) && database == MYSQL
96
100
  end
97
101
 
98
102
  check_pairs(pairs)
@@ -23,7 +23,6 @@ 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
27
26
 
28
27
  def_node_matcher :output?, <<~PATTERN
29
28
  (send nil? {:ap :p :pp :pretty_print :print :puts} ...)
@@ -40,7 +39,7 @@ module RuboCop
40
39
  PATTERN
41
40
 
42
41
  def on_send(node)
43
- return if ALLOWED_TYPES.include?(node.parent&.type)
42
+ return if node.parent&.call_type? || node.block_node
44
43
  return if !output?(node) && !io_output?(node)
45
44
 
46
45
  range = offense_range(node)
@@ -9,6 +9,24 @@ module RuboCop
9
9
  # element in an enumerable. When called on an Active Record relation, it
10
10
  # results in a more efficient query that only selects the necessary key.
11
11
  #
12
+ # NOTE: If the receiver's relation is not loaded and `pluck` is used inside an iteration,
13
+ # it may result in N+1 queries because `pluck` queries the database on each iteration.
14
+ # This cop ignores offenses for `map/collect` when they are suspected to be part of an iteration
15
+ # to prevent such potential issues.
16
+ #
17
+ # [source,ruby]
18
+ # ----
19
+ # users = User.all
20
+ # 5.times do
21
+ # users.map { |user| user[:foo] } # Only one query is executed
22
+ # end
23
+ #
24
+ # users = User.all
25
+ # 5.times do
26
+ # users.pluck(:id) # A query is executed on every iteration
27
+ # end
28
+ # ----
29
+ #
12
30
  # @safety
13
31
  # This cop is unsafe because model can use column aliases.
14
32
  #
@@ -38,30 +56,38 @@ module RuboCop
38
56
  minimum_target_rails_version 5.0
39
57
 
40
58
  def_node_matcher :pluck_candidate?, <<~PATTERN
41
- ({block numblock} (call _ {:map :collect}) $_argument (send lvar :[] $_key))
59
+ (any_block (call _ {:map :collect}) $_argument (send lvar :[] $_key))
42
60
  PATTERN
43
61
 
62
+ # rubocop:disable Metrics/AbcSize
44
63
  def on_block(node)
64
+ return if node.each_ancestor(:any_block).any?
65
+
45
66
  pluck_candidate?(node) do |argument, key|
46
67
  next if key.regexp_type? || !use_one_block_argument?(argument)
47
68
 
48
69
  match = if node.block_type?
49
70
  block_argument = argument.children.first.source
50
71
  use_block_argument_in_key?(block_argument, key)
51
- else # numblock
52
- argument == 1 && use_block_argument_in_key?('_1', key)
72
+ elsif node.numblock_type?
73
+ use_block_argument_in_key?('_1', key)
74
+ else # itblock
75
+ use_block_argument_in_key?('it', key)
53
76
  end
54
77
  next unless match
55
78
 
56
79
  register_offense(node, key)
57
80
  end
58
81
  end
82
+ # rubocop:enable Metrics/AbcSize
59
83
  alias on_numblock on_block
84
+ alias on_itblock on_block
60
85
 
61
86
  private
62
87
 
63
88
  def use_one_block_argument?(argument)
64
- return true if argument == 1 # Checks for numbered argument `_1`.
89
+ # Checks for numbered argument `_1` or `it block parameter.
90
+ return true if [1, :it].include?(argument)
65
91
 
66
92
  argument.respond_to?(:one?) && argument.one?
67
93
  end
@@ -7,17 +7,26 @@ module RuboCop
7
7
  # and can be replaced with `select`.
8
8
  #
9
9
  # Since `pluck` is an eager method and hits the database immediately,
10
- # using `select` helps to avoid additional database queries.
10
+ # using `select` helps to avoid additional database queries by running as
11
+ # a subquery.
11
12
  #
12
- # This cop has two different enforcement modes. When the `EnforcedStyle`
13
- # is `conservative` (the default) then only calls to `pluck` on a constant
14
- # (i.e. a model class) in the `where` is used as offenses.
13
+ # This cop has two modes of enforcement. When the `EnforcedStyle` is set
14
+ # to `conservative` (the default), only calls to `pluck` on a constant
15
+ # (e.g. a model class) within `where` are considered offenses.
15
16
  #
16
17
  # @safety
17
- # When the `EnforcedStyle` is `aggressive` then all calls to `pluck` in the
18
- # `where` is used as offenses. This may lead to false positives
19
- # as the cop cannot replace to `select` between calls to `pluck` on an
20
- # `ActiveRecord::Relation` instance vs a call to `pluck` on an `Array` instance.
18
+ # When `EnforcedStyle` is set to `aggressive`, all calls to `pluck`
19
+ # within `where` are considered offenses. This might lead to false
20
+ # positives because the check cannot distinguish between calls to
21
+ # `pluck` on an `ActiveRecord::Relation` instance and calls to `pluck`
22
+ # on an `Array` instance.
23
+ #
24
+ # Additionally, when using a subquery with the SQL `IN` operator,
25
+ # databases like PostgreSQL and MySQL can't optimize complex queries as
26
+ # well. They need to scan all records of the outer table against the
27
+ # subquery result sequentially, rather than using an index. This can
28
+ # cause significant performance issues compared to writing the query
29
+ # differently or using `pluck`.
21
30
  #
22
31
  # @example
23
32
  # # bad
@@ -10,25 +10,39 @@ module RuboCop
10
10
  # # bad
11
11
  # 3.day.ago
12
12
  # 1.months.ago
13
+ # 5.megabyte
14
+ # 1.gigabytes
13
15
  #
14
16
  # # good
15
17
  # 3.days.ago
16
18
  # 1.month.ago
19
+ # 5.megabytes
20
+ # 1.gigabyte
17
21
  class PluralizationGrammar < Base
18
22
  extend AutoCorrector
19
23
 
20
- SINGULAR_DURATION_METHODS = { second: :seconds,
21
- minute: :minutes,
22
- hour: :hours,
23
- day: :days,
24
- week: :weeks,
25
- fortnight: :fortnights,
26
- month: :months,
27
- year: :years }.freeze
28
-
29
- RESTRICT_ON_SEND = SINGULAR_DURATION_METHODS.keys + SINGULAR_DURATION_METHODS.values
30
-
31
- PLURAL_DURATION_METHODS = SINGULAR_DURATION_METHODS.invert.freeze
24
+ SINGULAR_METHODS = {
25
+ second: :seconds,
26
+ minute: :minutes,
27
+ hour: :hours,
28
+ day: :days,
29
+ week: :weeks,
30
+ fortnight: :fortnights,
31
+ month: :months,
32
+ year: :years,
33
+ byte: :bytes,
34
+ kilobyte: :kilobytes,
35
+ megabyte: :megabytes,
36
+ gigabyte: :gigabytes,
37
+ terabyte: :terabytes,
38
+ petabyte: :petabytes,
39
+ exabyte: :exabytes,
40
+ zettabyte: :zettabytes
41
+ }.freeze
42
+
43
+ RESTRICT_ON_SEND = SINGULAR_METHODS.keys + SINGULAR_METHODS.values
44
+
45
+ PLURAL_METHODS = SINGULAR_METHODS.invert.freeze
32
46
 
33
47
  MSG = 'Prefer `%<number>s.%<correct>s`.'
34
48
 
@@ -82,19 +96,19 @@ module RuboCop
82
96
  end
83
97
 
84
98
  def literal_number?(node)
85
- node && (node.int_type? || node.float_type?)
99
+ node&.type?(:int, :float)
86
100
  end
87
101
 
88
102
  def pluralize(method_name)
89
- SINGULAR_DURATION_METHODS.fetch(method_name.to_sym).to_s
103
+ SINGULAR_METHODS.fetch(method_name.to_sym).to_s
90
104
  end
91
105
 
92
106
  def singularize(method_name)
93
- PLURAL_DURATION_METHODS.fetch(method_name.to_sym).to_s
107
+ PLURAL_METHODS.fetch(method_name.to_sym).to_s
94
108
  end
95
109
 
96
110
  def duration_method?(method_name)
97
- SINGULAR_DURATION_METHODS.key?(method_name) || PLURAL_DURATION_METHODS.key?(method_name)
111
+ SINGULAR_METHODS.key?(method_name) || PLURAL_METHODS.key?(method_name)
98
112
  end
99
113
  end
100
114
  end
@@ -102,7 +102,7 @@ module RuboCop
102
102
  end
103
103
 
104
104
  def ignore_other_node?(node)
105
- node && (node.if_type? || node.rescue_type? || node.while_type?)
105
+ node&.type?(:if, :rescue, :while)
106
106
  end
107
107
 
108
108
  def message(node, receiver, other)
@@ -98,8 +98,6 @@ module RuboCop
98
98
  end
99
99
 
100
100
  def on_or(node)
101
- return unless cop_config['NilOrEmpty']
102
-
103
101
  exists_and_not_empty?(node) do |var1, var2|
104
102
  return unless var1 == var2
105
103
 
@@ -112,7 +110,7 @@ module RuboCop
112
110
  def on_if(node)
113
111
  return unless cop_config['UnlessBlank']
114
112
  return unless node.unless?
115
- return if node.else? && config.for_cop('Style/UnlessElse')['Enabled']
113
+ return if node.else? && config.cop_enabled?('Style/UnlessElse')
116
114
 
117
115
  unless_blank?(node) do |method_call, receiver|
118
116
  range = unless_condition(node, method_call)
@@ -3,35 +3,6 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Rails
6
- # TODO: In the future, please support only RuboCop 1.52+ and use `RuboCop::Cop::AllowedReceivers`:
7
- # https://github.com/rubocop/rubocop/blob/v1.52.0/lib/rubocop/cop/mixin/allowed_receivers.rb
8
- # At that time, this duplicated module implementation can be removed.
9
- module AllowedReceivers
10
- def allowed_receiver?(receiver)
11
- receiver_name = receiver_name(receiver)
12
-
13
- allowed_receivers.include?(receiver_name)
14
- end
15
-
16
- def receiver_name(receiver)
17
- return receiver_name(receiver.receiver) if receiver.receiver && !receiver.receiver.const_type?
18
-
19
- if receiver.send_type?
20
- if receiver.receiver
21
- "#{receiver_name(receiver.receiver)}.#{receiver.method_name}"
22
- else
23
- receiver.method_name.to_s
24
- end
25
- else
26
- receiver.source
27
- end
28
- end
29
-
30
- def allowed_receivers
31
- cop_config.fetch('AllowedReceivers', [])
32
- end
33
- end
34
-
35
6
  # Detect redundant `all` used as a receiver for Active Record query methods.
36
7
  #
37
8
  # For the methods `delete_all` and `destroy_all`, this cop will only check cases where the receiver is a model.
@@ -203,7 +174,7 @@ module RuboCop
203
174
  parent = node.parent
204
175
  return false unless POSSIBLE_ENUMERABLE_BLOCK_METHODS.include?(parent.method_name)
205
176
 
206
- parent.parent&.block_type? || parent.parent&.numblock_type? || parent.first_argument&.block_pass_type?
177
+ parent.block_literal? || parent.first_argument&.block_pass_type?
207
178
  end
208
179
 
209
180
  def sensitive_association_method?(node)
@@ -40,7 +40,7 @@ module RuboCop
40
40
  def on_send(node)
41
41
  association_with_foreign_key(node) do |type, name, options, foreign_key_pair, foreign_key|
42
42
  if redundant?(node, type, name, options, foreign_key)
43
- add_offense(foreign_key_pair.source_range) do |corrector|
43
+ add_offense(foreign_key_pair) do |corrector|
44
44
  range = range_with_surrounding_space(foreign_key_pair.source_range, side: :left)
45
45
  range = range_with_surrounding_comma(range, :left)
46
46
 
@@ -39,6 +39,9 @@ module RuboCop
39
39
  MSG = 'Remove explicit presence validation for %<association>s.'
40
40
  RESTRICT_ON_SEND = %i[validates].freeze
41
41
 
42
+ # From https://github.com/rails/rails/blob/7a0bf93b9dd291c7f61121a41b3a813ac8857e6a/activemodel/lib/active_model/validations/validates.rb#L157-L159
43
+ NON_VALIDATION_OPTIONS = %i[if unless on allow_blank allow_nil strict].freeze
44
+
42
45
  minimum_target_rails_version 5.0
43
46
 
44
47
  # @!method presence_validation?(node)
@@ -170,6 +173,12 @@ module RuboCop
170
173
 
171
174
  def on_send(node)
172
175
  presence_validation?(node) do |all_keys, options, presence|
176
+ # If presence is the only validation option and other non-validation options
177
+ # are present, removing it will cause rails to error.
178
+ used_option_keys = options.keys.select(&:sym_type?).map(&:value)
179
+ remaining_validations = used_option_keys - NON_VALIDATION_OPTIONS - [:presence]
180
+ return if remaining_validations.none? && options.keys.length > 1
181
+
173
182
  keys = non_optional_belongs_to(node.parent, all_keys)
174
183
  return if keys.none?
175
184
 
@@ -78,25 +78,29 @@ module RuboCop
78
78
 
79
79
  send_nodes.each do |send_node|
80
80
  receiver = send_node.receiver
81
- add_offense(receiver.source_range) do |corrector|
81
+ add_offense(receiver) do |corrector|
82
82
  autocorrect(corrector, send_node, node)
83
83
  end
84
84
  end
85
85
  end
86
86
 
87
87
  alias on_numblock on_block
88
+ alias on_itblock on_block
88
89
 
89
90
  private
90
91
 
91
92
  def autocorrect(corrector, send_node, node)
92
93
  corrector.remove(send_node.receiver)
93
94
  corrector.remove(send_node.loc.dot)
94
- corrector.remove(block_argument_range(send_node)) unless node.numblock_type?
95
+ corrector.remove(block_argument_range(send_node)) if node.block_type?
95
96
  end
96
97
 
98
+ # rubocop:disable Metrics/AbcSize
97
99
  def redundant_receiver?(send_nodes, node)
98
100
  proc = if node.numblock_type?
99
101
  ->(n) { n.receiver.lvar_type? && n.receiver.source == '_1' }
102
+ elsif node.itblock_type?
103
+ ->(n) { n.receiver.lvar_type? && n.receiver.source == 'it' }
100
104
  else
101
105
  return false if node.arguments.empty?
102
106
 
@@ -106,6 +110,7 @@ module RuboCop
106
110
 
107
111
  send_nodes.all?(&proc)
108
112
  end
113
+ # rubocop:enable Metrics/AbcSize
109
114
 
110
115
  def block_argument_range(node)
111
116
  block_node = node.each_ancestor(:block).first
@@ -40,10 +40,10 @@ module RuboCop
40
40
 
41
41
  def on_send(node)
42
42
  association_with_reflection(node) do |reflection_class_name|
43
- return if reflection_class_name.value.send_type? && reflection_class_name.value.receiver.nil?
43
+ return if reflection_class_name.value.send_type? && !reflection_class_name.value.receiver&.const_type?
44
44
  return if reflection_class_name.value.lvar_type? && str_assigned?(reflection_class_name)
45
45
 
46
- add_offense(reflection_class_name.source_range) do |corrector|
46
+ add_offense(reflection_class_name) do |corrector|
47
47
  autocorrect(corrector, reflection_class_name)
48
48
  end
49
49
  end
@@ -76,7 +76,7 @@ module RuboCop
76
76
  def autocorrect(corrector, class_config)
77
77
  class_value = class_config.value
78
78
  replacement = const_or_string(class_value)
79
- return unless replacement.present?
79
+ return unless replacement
80
80
 
81
81
  corrector.replace(class_value, replacement.source.inspect)
82
82
  end
@@ -90,7 +90,7 @@ module RuboCop
90
90
  end
91
91
 
92
92
  def nested_relative_date(node, &callback)
93
- return if node.nil? || node.block_type?
93
+ return if node.nil? || node.any_block_type?
94
94
 
95
95
  node.each_child_node do |child|
96
96
  nested_relative_date(child, &callback)
@@ -53,9 +53,12 @@ module RuboCop
53
53
  node.pairs.find { |p| p.key.value.to_sym == :content_type }
54
54
  end
55
55
 
56
- def compatible_content_type?(node)
57
- (node && node.value.value == 'text/plain') ||
58
- (!node && !cop_config['ContentTypeCompatibility'])
56
+ def compatible_content_type?(pair_node)
57
+ if pair_node.nil?
58
+ !cop_config['ContentTypeCompatibility']
59
+ elsif pair_node.value.respond_to?(:value)
60
+ pair_node.value.value == 'text/plain'
61
+ end
59
62
  end
60
63
 
61
64
  def replacement(rest_options, option_value)
@@ -34,7 +34,7 @@ module RuboCop
34
34
  referer?(node) do
35
35
  return unless node.method?(wrong_method_name)
36
36
 
37
- add_offense(node.source_range) do |corrector|
37
+ add_offense(node) do |corrector|
38
38
  corrector.replace(node, "request.#{style}")
39
39
  end
40
40
  end
@@ -205,6 +205,7 @@ module RuboCop
205
205
  end
206
206
 
207
207
  alias on_numblock on_block
208
+ alias on_itblock on_block
208
209
 
209
210
  private
210
211
 
@@ -215,8 +216,10 @@ module RuboCop
215
216
  end
216
217
 
217
218
  def check_drop_table_node(node)
219
+ return unless (last_argument = node.last_argument)
220
+
218
221
  drop_table_call(node) do
219
- unless node.parent.block_type? || node.last_argument.block_pass_type?
222
+ unless node.parent.any_block_type? || last_argument.block_pass_type?
220
223
  add_offense(node, message: format(MSG, action: 'drop_table(without block)'))
221
224
  end
222
225
  end
@@ -23,6 +23,8 @@ module RuboCop
23
23
  # File.binread(Rails.root.join('db', 'schema.rb'))
24
24
  # File.write(Rails.root.join('db', 'schema.rb'), content)
25
25
  # File.binwrite(Rails.root.join('db', 'schema.rb'), content)
26
+ # Dir.glob(Rails.root.join('db', 'schema.rb'))
27
+ # Dir[Rails.root.join('db', 'schema.rb')]
26
28
  #
27
29
  # # good
28
30
  # Rails.root.join('db', 'schema.rb').open
@@ -31,14 +33,15 @@ module RuboCop
31
33
  # Rails.root.join('db', 'schema.rb').binread
32
34
  # Rails.root.join('db', 'schema.rb').write(content)
33
35
  # Rails.root.join('db', 'schema.rb').binwrite(content)
36
+ # Rails.root.glob("db/schema.rb")
34
37
  #
35
38
  class RootPathnameMethods < Base # rubocop:disable Metrics/ClassLength
36
39
  extend AutoCorrector
37
40
  include RangeHelp
38
41
 
39
- MSG = '`%<rails_root>s` is a `Pathname` so you can just append `#%<method>s`.'
42
+ MSG = '`%<rails_root>s` is a `Pathname`, so you can use `%<replacement>s`.'
40
43
 
41
- DIR_GLOB_METHODS = %i[glob].to_set.freeze
44
+ DIR_GLOB_METHODS = %i[[] glob].to_set.freeze
42
45
 
43
46
  DIR_NON_GLOB_METHODS = %i[
44
47
  children
@@ -171,7 +174,7 @@ module RuboCop
171
174
 
172
175
  def_node_matcher :dir_glob?, <<~PATTERN
173
176
  (send
174
- (const {cbase nil?} :Dir) :glob ...)
177
+ (const {cbase nil?} :Dir) DIR_GLOB_METHODS ...)
175
178
  PATTERN
176
179
 
177
180
  def_node_matcher :rails_root_pathname?, <<~PATTERN
@@ -188,13 +191,14 @@ module RuboCop
188
191
 
189
192
  def on_send(node)
190
193
  evidence(node) do |method, path, args, rails_root|
191
- add_offense(node, message: format(MSG, method: method, rails_root: rails_root.source)) do |corrector|
192
- replacement = if dir_glob?(node)
193
- build_path_glob_replacement(path, method)
194
- else
195
- build_path_replacement(path, method, args)
196
- end
194
+ replacement = if dir_glob?(node)
195
+ build_path_glob_replacement(path)
196
+ else
197
+ build_path_replacement(path, method, args)
198
+ end
197
199
 
200
+ message = format(MSG, rails_root: rails_root.source, replacement: replacement)
201
+ add_offense(node, message: message) do |corrector|
198
202
  corrector.replace(node, replacement)
199
203
  end
200
204
  end
@@ -217,12 +221,12 @@ module RuboCop
217
221
  end
218
222
  end
219
223
 
220
- def build_path_glob_replacement(path, method)
224
+ def build_path_glob_replacement(path)
221
225
  receiver = range_between(path.source_range.begin_pos, path.children.first.loc.selector.end_pos).source
222
226
 
223
227
  argument = path.arguments.one? ? path.first_argument.source : join_arguments(path.arguments)
224
228
 
225
- "#{receiver}.#{method}(#{argument})"
229
+ "#{receiver}.glob(#{argument})"
226
230
  end
227
231
 
228
232
  def build_path_replacement(path, method, args)
@@ -233,7 +237,12 @@ module RuboCop
233
237
  end
234
238
 
235
239
  replacement = "#{path_replacement}.#{method}"
236
- replacement += "(#{args.map(&:source).join(', ')})" unless args.empty?
240
+
241
+ if args.any?
242
+ formatted_args = args.map { |arg| arg.array_type? ? "*#{arg.source}" : arg.source }
243
+ replacement += "(#{formatted_args.join(', ')})"
244
+ end
245
+
237
246
  replacement
238
247
  end
239
248
 
@@ -182,7 +182,7 @@ module RuboCop
182
182
  def right_assignment_node(assignment)
183
183
  node = assignment.node.child_nodes.first
184
184
 
185
- return node unless node&.block_type?
185
+ return node unless node&.any_block_type?
186
186
 
187
187
  node.send_node
188
188
  end
@@ -244,11 +244,11 @@ module RuboCop
244
244
  end
245
245
 
246
246
  def operator_or_single_negative?(node)
247
- node.or_type? || node.and_type? || single_negative?(node)
247
+ node.operator_keyword? || single_negative?(node)
248
248
  end
249
249
 
250
250
  def conditional?(parent)
251
- parent.if_type? || parent.case_type?
251
+ parent.type?(:if, :case)
252
252
  end
253
253
 
254
254
  def deparenthesize(node)
@@ -305,7 +305,7 @@ module RuboCop
305
305
 
306
306
  node = assignable_node(node)
307
307
  method, sibling_index = find_method_with_sibling_index(node.parent)
308
- return false unless method && (method.def_type? || method.block_type?)
308
+ return false unless method&.type?(:def, :any_block)
309
309
 
310
310
  method.children.size == node.sibling_index + sibling_index
311
311
  end
@@ -324,12 +324,13 @@ module RuboCop
324
324
 
325
325
  def explicit_return?(node)
326
326
  ret = assignable_node(node).parent
327
- ret && (ret.return_type? || ret.next_type?)
327
+ ret&.type?(:return, :next)
328
328
  end
329
329
 
330
330
  def return_value_assigned?(node)
331
- assignment = assignable_node(node).parent
332
- assignment&.lvasgn_type?
331
+ return false unless (assignment = assignable_node(node).parent)
332
+
333
+ assignment.assignment?
333
334
  end
334
335
 
335
336
  def persist_method?(node, methods = RESTRICT_ON_SEND)
@@ -23,6 +23,7 @@ module RuboCop
23
23
  #
24
24
  class SchemaComment < Base
25
25
  include ActiveRecordMigrationsHelper
26
+ include MigrationsHelper
26
27
 
27
28
  COLUMN_MSG = 'New database column without `comment`.'
28
29
  TABLE_MSG = 'New database table without `comment`.'
@@ -38,7 +39,7 @@ module RuboCop
38
39
 
39
40
  # @!method comment_present?(node)
40
41
  def_node_matcher :comment_present?, <<~PATTERN
41
- (hash <(pair {(sym :comment) (str "comment")} (_ [present?])) ...>)
42
+ (hash <(pair {(sym :comment) (str "comment")} !{nil (str blank?)}) ...>)
42
43
  PATTERN
43
44
 
44
45
  # @!method add_column?(node)
@@ -40,12 +40,13 @@ module RuboCop
40
40
  autocorrect(corrector, select_node, node, preferred_method)
41
41
  end
42
42
  end
43
+ alias on_csend on_send
43
44
 
44
45
  private
45
46
 
46
47
  def find_select_node(node, column_name)
47
48
  node.descendants.detect do |select_candidate|
48
- next if !select_candidate.send_type? || !select_candidate.method?(:select)
49
+ next if !select_candidate.call_type? || !select_candidate.method?(:select)
49
50
 
50
51
  match_column_name?(select_candidate, column_name)
51
52
  end
@@ -53,7 +54,7 @@ module RuboCop
53
54
 
54
55
  # rubocop:disable Metrics/AbcSize
55
56
  def autocorrect(corrector, select_node, node, preferred_method)
56
- corrector.remove(select_node.loc.dot || node.loc.dot)
57
+ corrector.remove(select_node.parent.loc.dot)
57
58
  corrector.remove(select_node.loc.selector.begin.join(select_node.source_range.end))
58
59
  corrector.replace(node.loc.selector.begin.join(node.source_range.end), preferred_method)
59
60
  end
@@ -61,7 +61,7 @@ module RuboCop
61
61
  def_node_matcher :good_touch?, <<~PATTERN
62
62
  {
63
63
  (send (const {nil? cbase} :FileUtils) :touch ...)
64
- (send _ :touch {true false})
64
+ (send _ :touch boolean)
65
65
  }
66
66
  PATTERN
67
67
 
@@ -100,7 +100,8 @@ module RuboCop
100
100
  end
101
101
 
102
102
  def forbidden_methods
103
- obsolete_result = cop_config['Blacklist']
103
+ # TODO: Remove when RuboCop Rails 3 releases.
104
+ obsolete_result = cop_config['Blacklist'] # rubocop:disable InternalAffairs/UndefinedConfig
104
105
  if obsolete_result
105
106
  warn '`Blacklist` has been renamed to `ForbiddenMethods`.' unless @displayed_forbidden_warning
106
107
  @displayed_forbidden_warning = true
@@ -111,7 +112,8 @@ module RuboCop
111
112
  end
112
113
 
113
114
  def allowed_methods
114
- obsolete_result = cop_config['Whitelist']
115
+ # TODO: Remove when RuboCop Rails 3 releases.
116
+ obsolete_result = cop_config['Whitelist'] # rubocop:disable InternalAffairs/UndefinedConfig
115
117
  if obsolete_result
116
118
  warn '`Whitelist` has been renamed to `AllowedMethods`.' unless @displayed_allowed_warning
117
119
  @displayed_allowed_warning = true