rubocop-rails 2.26.0 → 2.29.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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +51 -1
  4. data/config/default.yml +30 -6
  5. data/lib/rubocop/cop/mixin/index_method.rb +12 -5
  6. data/lib/rubocop/cop/mixin/routes_helper.rb +20 -0
  7. data/lib/rubocop/cop/mixin/target_rails_version.rb +3 -5
  8. data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +5 -5
  9. data/lib/rubocop/cop/rails/action_order.rb +1 -5
  10. data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +1 -5
  11. data/lib/rubocop/cop/rails/add_column_index.rb +1 -0
  12. data/lib/rubocop/cop/rails/application_record.rb +4 -0
  13. data/lib/rubocop/cop/rails/bulk_change_table.rb +1 -0
  14. data/lib/rubocop/cop/rails/compact_blank.rb +8 -4
  15. data/lib/rubocop/cop/rails/dangerous_column_names.rb +2 -0
  16. data/lib/rubocop/cop/rails/duplicate_association.rb +8 -4
  17. data/lib/rubocop/cop/rails/enum_syntax.rb +18 -14
  18. data/lib/rubocop/cop/rails/env_local.rb +26 -3
  19. data/lib/rubocop/cop/rails/file_path.rb +26 -3
  20. data/lib/rubocop/cop/rails/http_positional_arguments.rb +7 -0
  21. data/lib/rubocop/cop/rails/index_by.rb +28 -12
  22. data/lib/rubocop/cop/rails/index_with.rb +28 -12
  23. data/lib/rubocop/cop/rails/match_route.rb +1 -9
  24. data/lib/rubocop/cop/rails/multiple_route_paths.rb +50 -0
  25. data/lib/rubocop/cop/rails/not_null_column.rb +6 -2
  26. data/lib/rubocop/cop/rails/pluck.rb +20 -0
  27. data/lib/rubocop/cop/rails/pluralization_grammar.rb +1 -1
  28. data/lib/rubocop/cop/rails/present.rb +0 -2
  29. data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +1 -30
  30. data/lib/rubocop/cop/rails/redundant_foreign_key.rb +1 -1
  31. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +1 -1
  32. data/lib/rubocop/cop/rails/reflection_class_name.rb +1 -1
  33. data/lib/rubocop/cop/rails/request_referer.rb +1 -1
  34. data/lib/rubocop/cop/rails/reversible_migration.rb +3 -1
  35. data/lib/rubocop/cop/rails/root_pathname_methods.rb +17 -8
  36. data/lib/rubocop/cop/rails/save_bang.rb +4 -3
  37. data/lib/rubocop/cop/rails/schema_comment.rb +1 -0
  38. data/lib/rubocop/cop/rails/select_map.rb +3 -2
  39. data/lib/rubocop/cop/rails/skips_model_validations.rb +4 -2
  40. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +1 -1
  41. data/lib/rubocop/cop/rails/strong_parameters_expect.rb +104 -0
  42. data/lib/rubocop/cop/rails/three_state_boolean_column.rb +2 -1
  43. data/lib/rubocop/cop/rails/time_zone.rb +13 -6
  44. data/lib/rubocop/cop/rails/transaction_exit_statement.rb +5 -0
  45. data/lib/rubocop/cop/rails/where_equals.rb +6 -1
  46. data/lib/rubocop/cop/rails/where_not.rb +6 -1
  47. data/lib/rubocop/cop/rails/where_range.rb +7 -2
  48. data/lib/rubocop/cop/rails_cops.rb +3 -0
  49. data/lib/rubocop/rails/migration_file_skippable.rb +54 -0
  50. data/lib/rubocop/rails/version.rb +1 -1
  51. data/lib/rubocop/rails.rb +0 -1
  52. data/lib/rubocop-rails.rb +3 -0
  53. metadata +8 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c18cefb961b3100a871e2411c7a029244eda6998d793e3ece62aef1adcd4c571
4
- data.tar.gz: 24cde448ef91e689e05a2357c1aa0cbcc4d61630f1bb6e7fcaf8999ff7678105
3
+ metadata.gz: f6fa236fdf79709d4d0baec3c3ed01bc254e807dbd5ec39229374187b1d3d30c
4
+ data.tar.gz: 99b29d27dd97c1adc12efb3d60a55370f6c4ad363bd6de51ca11ac33f3b1cd8a
5
5
  SHA512:
6
- metadata.gz: 7be18be7c11552f4a8d460e4833b57881ec3244d3abc7509ef3f9aa78533e3ff77e0ac60cd1737c608c8a724aed4b37fd1faed06ccb689aabf1e0a21970beef6
7
- data.tar.gz: fb09716c5cb563cff4eaa33e68f0657424dce2650a9c7da8e37e6876ee8ee12a942ef2b096cb5604ee5a457a7f951dbd2198d290d0f5e0486c04b36e5a2679bc
6
+ metadata.gz: 422221a038428edaf91477ef0ede6939023a686f3a62c14242efbfa1cb31be9d8f504eaf15ac103875696b5584b690cabcc86a4041b5c42cfd4480aa4ad27dbd
7
+ data.tar.gz: 8240dcae47150b3137a980e731d69133df5e54364b62014d296b51715b1e3f47077b828d4f256191c79d8a14121e0d5cce9b06857f54b632e398216290e31f29
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-23 Bozhidar Batsov
1
+ Copyright (c) 2012-25 Bozhidar Batsov
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -63,9 +63,45 @@ RuboCop::RakeTask.new do |task|
63
63
  end
64
64
  ```
65
65
 
66
+ ## RuboCop Rails configuration
67
+
68
+ The following settings specific to RuboCop Rails can be configured in `.rubocop.yml`.
69
+
70
+ ### `AllCops: TargetRailsVersion`
71
+
72
+ What version of Rails is the inspected code using? If a value is specified
73
+ for `TargetRailsVersion` then it is used. Acceptable values are specified
74
+ as a float (e.g., 7.2); the patch version of Rails should not be included.
75
+
76
+ ```yaml
77
+ AllCops:
78
+ TargetRailsVersion: 7.2
79
+ ```
80
+
81
+ If `TargetRailsVersion` is not set, RuboCop will parse the Gemfile.lock or
82
+ gems.locked file to find the version of Rails that has been bound to the
83
+ application. If neither of those files exist, RuboCop will use Rails 5.0
84
+ as the default.
85
+
86
+ ### `AllCops: MigratedSchemaVersion`
87
+
88
+ By specifying the `MigratedSchemaVersion` option, migration files that have already been run can be ignored.
89
+ When `MigratedSchemaVersion: '20241225000000'` is set, migration files lower than or equal to '20241225000000' will be ignored.
90
+ For example, to ignore db/migrate/20241225000000_create_articles.rb and earlier migrations you would configure it the following way:
91
+
92
+ ```yaml
93
+ AllCops:
94
+ MigratedSchemaVersion: '20241225000000'
95
+ ```
96
+
97
+ This prevents inspecting schema settings for already applied migration files.
98
+ Changing already applied migrations should be avoided because it can lead to the schema getting out of sync
99
+ between your local copy and what it actually is in production, depending on when `bin/rails db:migrate` was executed.
100
+ If you want to modify your schema to comply with the cops, you should instead create new migrations.
101
+
66
102
  ## Rails configuration tip
67
103
 
68
- If you are using Rails 6.1 or newer, add the following `config.generators.after_generate` setting to
104
+ In Rails 6.1+, add the following `config.generators.after_generate` setting to
69
105
  your `config/environments/development.rb` to apply RuboCop autocorrection to code generated by `bin/rails g`.
70
106
 
71
107
  ```ruby
@@ -84,6 +120,20 @@ It uses `rubocop -A` to apply `Style/FrozenStringLiteralComment` and other unsaf
84
120
  `rubocop -A` is unsafe autocorrection, but code generated by default is simple and less likely to
85
121
  be incompatible with `rubocop -A`. If you have problems you can replace it with `rubocop -a` instead.
86
122
 
123
+ In Rails 7.2+, it is recommended to use `config.generators.apply_rubocop_autocorrect_after_generate!` instead of the above setting:
124
+
125
+ ```diff
126
+ # config/environments/development.rb
127
+ Rails.application.configure do
128
+ (snip)
129
+ # Apply autocorrection by RuboCop to files generated by `bin/rails generate`.
130
+ - # config.generators.apply_rubocop_autocorrect_after_generate!
131
+ + config.generators.apply_rubocop_autocorrect_after_generate!
132
+ end
133
+ ```
134
+
135
+ You only need to uncomment.
136
+
87
137
  ## The Cops
88
138
 
89
139
  All cops are located under
data/config/default.yml CHANGED
@@ -17,14 +17,18 @@ AllCops:
17
17
  # Enable checking Active Support extensions.
18
18
  # See: https://docs.rubocop.org/rubocop/configuration.html#enable-checking-active-support-extensions
19
19
  ActiveSupportExtensionsEnabled: true
20
- # What version of Rails is the inspected code using? If a value is specified
21
- # for TargetRailsVersion then it is used. Acceptable values are specified
22
- # as a float (i.e. 5.1); the patch version of Rails should not be included.
23
- # If TargetRailsVersion is not set, RuboCop will parse the Gemfile.lock or
20
+ # What version of Rails is the inspected code using? If a value is specified
21
+ # for `TargetRailsVersion` then it is used. Acceptable values are specified
22
+ # as a float (e.g., 7.2); the patch version of Rails should not be included.
23
+ # If `TargetRailsVersion` is not set, RuboCop will parse the Gemfile.lock or
24
24
  # gems.locked file to find the version of Rails that has been bound to the
25
- # application. If neither of those files exist, RuboCop will use Rails 5.0
25
+ # application. If neither of those files exist, RuboCop will use Rails 5.0
26
26
  # as the default.
27
27
  TargetRailsVersion: ~
28
+ # By specifying `MigratedSchemaVersion` option, migration files that have been migrated can be ignored.
29
+ # When `MigratedSchemaVersion: '20241231000000'` is set. Migration files lower than or equal to '20250101000000' will be ignored.
30
+ # For example, this is the timestamp in db/migrate/20250101000000_create_articles.rb.
31
+ MigratedSchemaVersion: ~
28
32
 
29
33
  Lint/NumberConversion:
30
34
  # Add Rails' duration methods to the ignore list for `Lint/NumberConversion`
@@ -212,7 +216,9 @@ Rails/ApplicationRecord:
212
216
  Enabled: true
213
217
  SafeAutoCorrect: false
214
218
  VersionAdded: '0.49'
215
- VersionChanged: '2.5'
219
+ VersionChanged: '2.26'
220
+ Exclude:
221
+ - db/**/*.rb
216
222
 
217
223
  Rails/ArelStar:
218
224
  Description: 'Enforces `Arel.star` instead of `"*"` for expanded columns.'
@@ -692,6 +698,15 @@ Rails/MigrationClassName:
692
698
  Include:
693
699
  - db/**/*.rb
694
700
 
701
+ Rails/MultipleRoutePaths:
702
+ Description: 'Checks for mapping a route with multiple paths, which is deprecated and will be removed in Rails 8.1.'
703
+ Enabled: pending
704
+ Severity: warning
705
+ VersionAdded: '2.29'
706
+ Include:
707
+ - config/routes.rb
708
+ - config/routes/**/*.rb
709
+
695
710
  Rails/NegateInclude:
696
711
  Description: 'Prefer `collection.exclude?(obj)` over `!collection.include?(obj)`.'
697
712
  StyleGuide: 'https://rails.rubystyle.guide#exclude'
@@ -1066,6 +1081,15 @@ Rails/StripHeredoc:
1066
1081
  Enabled: pending
1067
1082
  VersionAdded: '2.15'
1068
1083
 
1084
+ Rails/StrongParametersExpect:
1085
+ Description: 'Enforces the use of `ActionController::Parameters#expect` as a method for strong parameter handling.'
1086
+ Reference: 'https://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-expect'
1087
+ Enabled: pending
1088
+ Include:
1089
+ - app/controllers/**/*.rb
1090
+ SafeAutoCorrect: false
1091
+ VersionAdded: '2.29'
1092
+
1069
1093
  Rails/TableNameAssignment:
1070
1094
  Description: >-
1071
1095
  Do not use `self.table_name =`. Use Inflections or `table_name_prefix` instead.
@@ -6,7 +6,7 @@ module RuboCop
6
6
  module IndexMethod # rubocop:disable Metrics/ModuleLength
7
7
  RESTRICT_ON_SEND = %i[each_with_object to_h map collect []].freeze
8
8
 
9
- def on_block(node) # rubocop:todo InternalAffairs/NumblockHandler
9
+ def on_block(node)
10
10
  on_bad_each_with_object(node) do |*match|
11
11
  handle_possible_offense(node, match, 'each_with_object')
12
12
  end
@@ -18,6 +18,8 @@ module RuboCop
18
18
  end
19
19
  end
20
20
 
21
+ alias on_numblock on_block
22
+
21
23
  def on_send(node)
22
24
  on_bad_map_to_h(node) do |*match|
23
25
  handle_possible_offense(node, match, 'map { ... }.to_h')
@@ -122,9 +124,9 @@ module RuboCop
122
124
  end
123
125
 
124
126
  def self.from_map_to_h(node, match)
125
- strip_trailing_chars = 0
126
-
127
- unless node.parent&.block_type?
127
+ if node.block_literal?
128
+ strip_trailing_chars = 0
129
+ else
128
130
  map_range = node.children.first.source_range
129
131
  node_range = node.source_range
130
132
  strip_trailing_chars = node_range.end_pos - map_range.end_pos
@@ -153,11 +155,16 @@ module RuboCop
153
155
  end
154
156
 
155
157
  def set_new_arg_name(transformed_argname, corrector)
158
+ return if block_node.numblock_type?
159
+
156
160
  corrector.replace(block_node.arguments, "|#{transformed_argname}|")
157
161
  end
158
162
 
159
163
  def set_new_body_expression(transforming_body_expr, corrector)
160
- corrector.replace(block_node.body, transforming_body_expr.source)
164
+ body_source = transforming_body_expr.source
165
+ body_source = "{ #{body_source} }" if transforming_body_expr.hash_type? && !transforming_body_expr.braces?
166
+
167
+ corrector.replace(block_node.body, body_source)
161
168
  end
162
169
  end
163
170
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for cops working with routes.
6
+ module RoutesHelper
7
+ extend NodePattern::Macros
8
+
9
+ HTTP_METHODS = %i[get post put patch delete].freeze
10
+
11
+ def_node_matcher :routes_draw?, <<~PATTERN
12
+ (send (send _ :routes) :draw)
13
+ PATTERN
14
+
15
+ def within_routes?(node)
16
+ node.each_ancestor(:block).any? { |block| routes_draw?(block.send_node) }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -7,6 +7,9 @@ module RuboCop
7
7
  # Informs the base RuboCop gem that it the Rails version is checked via `requires_gem` API,
8
8
  # without needing to call this `#support_target_rails_version` method.
9
9
  USES_REQUIRES_GEM_API = true
10
+ # Look for `railties` instead of `rails`, to support apps that only use a subset of `rails`
11
+ # See https://github.com/rubocop/rubocop/pull/11289
12
+ TARGET_GEM_NAME = 'railties' # :nodoc:
10
13
 
11
14
  def minimum_target_rails_version(version)
12
15
  if respond_to?(:requires_gem)
@@ -33,11 +36,6 @@ module RuboCop
33
36
  @minimum_target_rails_version <= version
34
37
  end
35
38
  end
36
-
37
- # Look for `railties` instead of `rails`, to support apps that only use a subset of `rails`
38
- # See https://github.com/rubocop/rubocop/pull/11289
39
- TARGET_GEM_NAME = 'railties'
40
- private_constant :TARGET_GEM_NAME
41
39
  end
42
40
  end
43
41
  end
@@ -72,13 +72,13 @@ module RuboCop
72
72
  if (node = context.each_ancestor(:if, :rescue).first)
73
73
  return false if use_redirect_to?(context)
74
74
 
75
- context = node.rescue_type? ? node.parent : node
75
+ context = node
76
+ elsif context.right_siblings.empty?
77
+ return true
76
78
  end
79
+ context = context.right_siblings
77
80
 
78
- siblings = context.right_siblings
79
- return true if siblings.empty?
80
-
81
- siblings.compact.any? do |render_candidate|
81
+ context.compact.any? do |render_candidate|
82
82
  render?(render_candidate)
83
83
  end
84
84
  end
@@ -92,11 +92,7 @@ module RuboCop
92
92
  end
93
93
 
94
94
  def range_with_comments(node)
95
- # rubocop:todo InternalAffairs/LocationExpression
96
- # Using `RuboCop::Ext::Comment#source_range` requires RuboCop > 1.46,
97
- # which introduces https://github.com/rubocop/rubocop/pull/11630.
98
- ranges = [node, *processed_source.ast_with_comments[node]].map { |comment| comment.loc.expression }
99
- # rubocop:enable InternalAffairs/LocationExpression
95
+ ranges = [node, *processed_source.ast_with_comments[node]].map(&:source_range)
100
96
  ranges.reduce do |result, range|
101
97
  add_range(result, range)
102
98
  end
@@ -123,11 +123,7 @@ module RuboCop
123
123
  end
124
124
 
125
125
  def inline_comment?(comment)
126
- # rubocop:todo InternalAffairs/LocationExpression
127
- # Using `RuboCop::Ext::Comment#source_range` requires RuboCop > 1.46,
128
- # which introduces https://github.com/rubocop/rubocop/pull/11630.
129
- !comment_line?(comment.loc.expression.source_line)
130
- # rubocop:enable InternalAffairs/LocationExpression
126
+ !comment_line?(comment.source_range.source_line)
131
127
  end
132
128
 
133
129
  def start_line_position(node)
@@ -19,6 +19,7 @@ module RuboCop
19
19
  class AddColumnIndex < Base
20
20
  extend AutoCorrector
21
21
  include RangeHelp
22
+ include MigrationsHelper
22
23
 
23
24
  MSG = '`add_column` does not accept an `index` key, use `add_index` instead.'
24
25
  RESTRICT_ON_SEND = %i[add_column].freeze
@@ -5,6 +5,10 @@ module RuboCop
5
5
  module Rails
6
6
  # Checks that models subclass `ApplicationRecord` with Rails 5.0.
7
7
  #
8
+ # It is a common practice to define models inside migrations in order to retain forward
9
+ # compatibility by avoiding loading any application code. And so migration files are excluded
10
+ # by default for this cop.
11
+ #
8
12
  # @safety
9
13
  # This cop's autocorrection is unsafe because it may let the logic from `ApplicationRecord`
10
14
  # sneak into an Active Record model that is not purposed to inherit logic common among other
@@ -65,6 +65,7 @@ module RuboCop
65
65
  # end
66
66
  class BulkChangeTable < Base
67
67
  include DatabaseTypeResolvable
68
+ include MigrationsHelper
68
69
 
69
70
  MSG_FOR_CHANGE_TABLE = <<~MSG.chomp
70
71
  You can combine alter queries using `bulk: true` options.
@@ -25,6 +25,8 @@ module RuboCop
25
25
  # collection.reject { |_k, v| v.blank? }
26
26
  # collection.select(&:present?)
27
27
  # collection.select { |_k, v| v.present? }
28
+ # collection.filter(&:present?)
29
+ # collection.filter { |_k, v| v.present? }
28
30
  #
29
31
  # # good
30
32
  # collection.compact_blank
@@ -44,7 +46,8 @@ module RuboCop
44
46
  extend TargetRailsVersion
45
47
 
46
48
  MSG = 'Use `%<preferred_method>s` instead.'
47
- RESTRICT_ON_SEND = %i[reject delete_if select keep_if].freeze
49
+ RESTRICT_ON_SEND = %i[reject delete_if select filter keep_if].freeze
50
+ DESTRUCTIVE_METHODS = %i[delete_if keep_if].freeze
48
51
 
49
52
  minimum_target_rails_version 6.1
50
53
 
@@ -64,19 +67,20 @@ module RuboCop
64
67
 
65
68
  def_node_matcher :select_with_block?, <<~PATTERN
66
69
  (block
67
- (send _ {:select :keep_if})
70
+ (send _ {:select :filter :keep_if})
68
71
  $(args ...)
69
72
  (send
70
73
  $(lvar _) :present?))
71
74
  PATTERN
72
75
 
73
76
  def_node_matcher :select_with_block_pass?, <<~PATTERN
74
- (send _ {:select :keep_if}
77
+ (send _ {:select :filter :keep_if}
75
78
  (block-pass
76
79
  (sym :present?)))
77
80
  PATTERN
78
81
 
79
82
  def on_send(node)
83
+ return if target_ruby_version < 2.6 && node.method?(:filter)
80
84
  return unless bad_method?(node)
81
85
 
82
86
  range = offense_range(node)
@@ -120,7 +124,7 @@ module RuboCop
120
124
  end
121
125
 
122
126
  def preferred_method(node)
123
- node.method?(:reject) || node.method?(:select) ? 'compact_blank' : 'compact_blank!'
127
+ DESTRUCTIVE_METHODS.include?(node.method_name) ? 'compact_blank!' : 'compact_blank'
124
128
  end
125
129
  end
126
130
  end
@@ -14,6 +14,8 @@ module RuboCop
14
14
  # # good
15
15
  # add_column :users, :saved
16
16
  class DangerousColumnNames < Base # rubocop:disable Metrics/ClassLength
17
+ include MigrationsHelper
18
+
17
19
  COLUMN_TYPE_METHOD_NAMES = %i[
18
20
  bigint
19
21
  binary
@@ -63,11 +63,15 @@ module RuboCop
63
63
  private
64
64
 
65
65
  def register_offense(name, nodes, message_template)
66
- nodes.each do |node|
67
- add_offense(node, message: format(message_template, name: name)) do |corrector|
68
- next if same_line?(nodes.last, node)
66
+ last_node = nodes.last
69
67
 
70
- corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
68
+ nodes.each_with_index do |node, index|
69
+ add_offense(node, message: format(message_template, name: name)) do |corrector|
70
+ if index.zero?
71
+ corrector.replace(node, last_node.source)
72
+ else
73
+ corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
74
+ end
71
75
  end
72
76
  end
73
77
  end
@@ -17,14 +17,19 @@ module RuboCop
17
17
  #
18
18
  class EnumSyntax < Base
19
19
  extend AutoCorrector
20
+ extend TargetRubyVersion
20
21
  extend TargetRailsVersion
21
22
 
23
+ minimum_target_ruby_version 3.0
22
24
  minimum_target_rails_version 7.0
23
25
 
24
26
  MSG = 'Enum defined with keyword arguments in `%<enum>s` enum declaration. Use positional arguments instead.'
25
27
  MSG_OPTIONS = 'Enum defined with deprecated options in `%<enum>s` enum declaration. Remove the `_` prefix.'
26
28
  RESTRICT_ON_SEND = %i[enum].freeze
27
- OPTION_NAMES = %w[prefix suffix scopes default].freeze
29
+
30
+ # From https://github.com/rails/rails/blob/v7.2.1/activerecord/lib/active_record/enum.rb#L231
31
+ OPTION_NAMES = %w[prefix suffix scopes default instance_methods].freeze
32
+ UNDERSCORED_OPTION_NAMES = OPTION_NAMES.map { |option| "_#{option}" }.freeze
28
33
 
29
34
  def_node_matcher :enum?, <<~PATTERN
30
35
  (send nil? :enum (hash $...))
@@ -34,14 +39,6 @@ module RuboCop
34
39
  (send nil? :enum $_ ${array hash} $_)
35
40
  PATTERN
36
41
 
37
- def_node_matcher :enum_values, <<~PATTERN
38
- (pair $_ ${array hash})
39
- PATTERN
40
-
41
- def_node_matcher :enum_options, <<~PATTERN
42
- (pair $_ $_)
43
- PATTERN
44
-
45
42
  def on_send(node)
46
43
  check_and_correct_keyword_args(node)
47
44
  check_enum_options(node)
@@ -52,10 +49,9 @@ module RuboCop
52
49
  def check_and_correct_keyword_args(node)
53
50
  enum?(node) do |pairs|
54
51
  pairs.each do |pair|
55
- key, values = enum_values(pair)
56
- next unless key
52
+ next if option_key?(pair)
57
53
 
58
- correct_keyword_args(node, key, values, pairs[1..])
54
+ correct_keyword_args(node, pair.key, pair.value, pairs[1..])
59
55
  end
60
56
  end
61
57
  end
@@ -63,9 +59,11 @@ module RuboCop
63
59
  def check_enum_options(node)
64
60
  enum_with_options?(node) do |key, _, options|
65
61
  options.children.each do |option|
66
- name, = enum_options(option)
62
+ next unless option_key?(option)
67
63
 
68
- add_offense(name, message: format(MSG_OPTIONS, enum: enum_name_value(key))) if name.source[0] == '_'
64
+ add_offense(option.key, message: format(MSG_OPTIONS, enum: enum_name_value(key))) do |corrector|
65
+ corrector.replace(option.key, option.key.source.delete_prefix('_'))
66
+ end
69
67
  end
70
68
  end
71
69
  end
@@ -107,6 +105,12 @@ module RuboCop
107
105
  end
108
106
  end
109
107
 
108
+ def option_key?(pair)
109
+ return false unless pair.respond_to?(:key)
110
+
111
+ UNDERSCORED_OPTION_NAMES.include?(pair.key.source)
112
+ end
113
+
110
114
  def correct_options(options)
111
115
  corrected_options = options.map do |pair|
112
116
  name = if pair.key.source[0] == '_'
@@ -19,20 +19,33 @@ module RuboCop
19
19
  extend TargetRailsVersion
20
20
 
21
21
  MSG = 'Use `Rails.env.local?` instead.'
22
+ MSG_NEGATED = 'Use `!Rails.env.local?` instead.'
22
23
  LOCAL_ENVIRONMENTS = %i[development? test?].to_set.freeze
23
24
 
24
25
  minimum_target_rails_version 7.1
25
26
 
26
- # @!method rails_env_local_candidate?(node)
27
- def_node_matcher :rails_env_local_candidate?, <<~PATTERN
27
+ # @!method rails_env_local_or?(node)
28
+ def_node_matcher :rails_env_local_or?, <<~PATTERN
28
29
  (or
29
30
  (send (send (const {cbase nil? } :Rails) :env) $%LOCAL_ENVIRONMENTS)
30
31
  (send (send (const {cbase nil? } :Rails) :env) $%LOCAL_ENVIRONMENTS)
31
32
  )
32
33
  PATTERN
33
34
 
35
+ # @!method rails_env_local_and?(node)
36
+ def_node_matcher :rails_env_local_and?, <<~PATTERN
37
+ (and
38
+ (send
39
+ (send (send (const {cbase nil? } :Rails) :env) $%LOCAL_ENVIRONMENTS)
40
+ :!)
41
+ (send
42
+ (send (send (const {cbase nil? } :Rails) :env) $%LOCAL_ENVIRONMENTS)
43
+ :!)
44
+ )
45
+ PATTERN
46
+
34
47
  def on_or(node)
35
- rails_env_local_candidate?(node) do |*environments|
48
+ rails_env_local_or?(node) do |*environments|
36
49
  next unless environments.to_set == LOCAL_ENVIRONMENTS
37
50
 
38
51
  add_offense(node) do |corrector|
@@ -40,6 +53,16 @@ module RuboCop
40
53
  end
41
54
  end
42
55
  end
56
+
57
+ def on_and(node)
58
+ rails_env_local_and?(node) do |*environments|
59
+ next unless environments.to_set == LOCAL_ENVIRONMENTS
60
+
61
+ add_offense(node, message: MSG_NEGATED) do |corrector|
62
+ corrector.replace(node, '!Rails.env.local?')
63
+ end
64
+ end
65
+ end
43
66
  end
44
67
  end
45
68
  end
@@ -66,6 +66,8 @@ module RuboCop
66
66
 
67
67
  def on_send(node)
68
68
  check_for_file_join_with_rails_root(node)
69
+ return unless node.receiver
70
+
69
71
  check_for_rails_root_join_with_slash_separated_path(node)
70
72
  check_for_rails_root_join_with_string_arguments(node)
71
73
  end
@@ -76,6 +78,7 @@ module RuboCop
76
78
  rails_root_index = find_rails_root_index(node)
77
79
  slash_node = node.children[rails_root_index + 1]
78
80
  return unless slash_node&.str_type? && slash_node.source.start_with?(File::SEPARATOR)
81
+ return unless node.children[rails_root_index].children.first.send_type?
79
82
 
80
83
  register_offense(node, require_to_s: false) do |corrector|
81
84
  autocorrect_slash_after_rails_root_in_dstr(corrector, node, rails_root_index)
@@ -97,7 +100,7 @@ module RuboCop
97
100
  return unless node.arguments.any? { |e| rails_root_nodes?(e) }
98
101
 
99
102
  register_offense(node, require_to_s: true) do |corrector|
100
- autocorrect_file_join(corrector, node)
103
+ autocorrect_file_join(corrector, node) unless node.first_argument.array_type?
101
104
  end
102
105
  end
103
106
 
@@ -170,7 +173,17 @@ module RuboCop
170
173
  end
171
174
 
172
175
  def autocorrect_file_join(corrector, node)
176
+ replace_receiver_with_rails_root(corrector, node)
177
+ remove_first_argument_with_comma(corrector, node)
178
+ process_arguments(corrector, node.arguments)
179
+ append_to_string_conversion(corrector, node)
180
+ end
181
+
182
+ def replace_receiver_with_rails_root(corrector, node)
173
183
  corrector.replace(node.receiver, 'Rails.root')
184
+ end
185
+
186
+ def remove_first_argument_with_comma(corrector, node)
174
187
  corrector.remove(
175
188
  range_with_surrounding_space(
176
189
  range_with_surrounding_comma(
@@ -180,9 +193,19 @@ module RuboCop
180
193
  side: :right
181
194
  )
182
195
  )
183
- node.arguments.filter(&:str_type?).each do |argument|
184
- corrector.replace(argument, argument.value.delete_prefix('/').inspect)
196
+ end
197
+
198
+ def process_arguments(corrector, arguments)
199
+ arguments.each do |argument|
200
+ if argument.str_type?
201
+ corrector.replace(argument, argument.value.delete_prefix('/').inspect)
202
+ elsif argument.array_type?
203
+ corrector.replace(argument, "*#{argument.source}")
204
+ end
185
205
  end
206
+ end
207
+
208
+ def append_to_string_conversion(corrector, node)
186
209
  corrector.insert_after(node, '.to_s')
187
210
  end
188
211
 
@@ -40,6 +40,10 @@ module RuboCop
40
40
  (hash (kwsplat _))
41
41
  PATTERN
42
42
 
43
+ def_node_matcher :forwarded_kwrestarg?, <<~PATTERN
44
+ (hash (forwarded-kwrestarg))
45
+ PATTERN
46
+
43
47
  def_node_matcher :include_rack_test_methods?, <<~PATTERN
44
48
  (send nil? :include
45
49
  (const
@@ -83,7 +87,9 @@ module RuboCop
83
87
  end
84
88
  end
85
89
 
90
+ # rubocop:disable Metrics/CyclomaticComplexity
86
91
  def needs_conversion?(data)
92
+ return false if data.forwarded_args_type? || forwarded_kwrestarg?(data)
87
93
  return true unless data.hash_type?
88
94
  return false if kwsplat_hash?(data)
89
95
 
@@ -91,6 +97,7 @@ module RuboCop
91
97
  special_keyword_arg?(pair.key) || (format_arg?(pair.key) && data.pairs.one?)
92
98
  end
93
99
  end
100
+ # rubocop:enable Metrics/CyclomaticComplexity
94
101
 
95
102
  def special_keyword_arg?(node)
96
103
  node.sym_type? && KEYWORD_ARGS.include?(node.value)