rubocop-rails 2.26.0 → 2.29.1

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