rubocop-rails 2.25.0 → 2.29.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +52 -2
  4. data/config/default.yml +47 -9
  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_order.rb +1 -5
  9. data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +1 -5
  10. data/lib/rubocop/cop/rails/add_column_index.rb +1 -0
  11. data/lib/rubocop/cop/rails/application_record.rb +4 -0
  12. data/lib/rubocop/cop/rails/bulk_change_table.rb +11 -4
  13. data/lib/rubocop/cop/rails/compact_blank.rb +29 -8
  14. data/lib/rubocop/cop/rails/dangerous_column_names.rb +2 -0
  15. data/lib/rubocop/cop/rails/date.rb +2 -2
  16. data/lib/rubocop/cop/rails/enum_hash.rb +31 -8
  17. data/lib/rubocop/cop/rails/enum_syntax.rb +130 -0
  18. data/lib/rubocop/cop/rails/enum_uniqueness.rb +29 -7
  19. data/lib/rubocop/cop/rails/env_local.rb +26 -3
  20. data/lib/rubocop/cop/rails/file_path.rb +4 -1
  21. data/lib/rubocop/cop/rails/http_positional_arguments.rb +7 -0
  22. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +1 -1
  23. data/lib/rubocop/cop/rails/index_by.rb +28 -12
  24. data/lib/rubocop/cop/rails/index_with.rb +28 -12
  25. data/lib/rubocop/cop/rails/link_to_blank.rb +2 -2
  26. data/lib/rubocop/cop/rails/match_route.rb +1 -9
  27. data/lib/rubocop/cop/rails/multiple_route_paths.rb +50 -0
  28. data/lib/rubocop/cop/rails/not_null_column.rb +8 -2
  29. data/lib/rubocop/cop/rails/pluck.rb +20 -0
  30. data/lib/rubocop/cop/rails/pluck_in_where.rb +17 -8
  31. data/lib/rubocop/cop/rails/pluralization_grammar.rb +29 -15
  32. data/lib/rubocop/cop/rails/present.rb +0 -2
  33. data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +1 -30
  34. data/lib/rubocop/cop/rails/redundant_foreign_key.rb +1 -1
  35. data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +9 -0
  36. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +1 -1
  37. data/lib/rubocop/cop/rails/reflection_class_name.rb +1 -1
  38. data/lib/rubocop/cop/rails/render_plain_text.rb +6 -3
  39. data/lib/rubocop/cop/rails/request_referer.rb +1 -1
  40. data/lib/rubocop/cop/rails/reversible_migration.rb +3 -1
  41. data/lib/rubocop/cop/rails/root_pathname_methods.rb +15 -11
  42. data/lib/rubocop/cop/rails/save_bang.rb +1 -1
  43. data/lib/rubocop/cop/rails/schema_comment.rb +1 -0
  44. data/lib/rubocop/cop/rails/select_map.rb +3 -2
  45. data/lib/rubocop/cop/rails/skips_model_validations.rb +7 -2
  46. data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +1 -1
  47. data/lib/rubocop/cop/rails/strong_parameters_expect.rb +104 -0
  48. data/lib/rubocop/cop/rails/three_state_boolean_column.rb +2 -1
  49. data/lib/rubocop/cop/rails/time_zone.rb +13 -6
  50. data/lib/rubocop/cop/rails/transaction_exit_statement.rb +5 -0
  51. data/lib/rubocop/cop/rails/validation.rb +4 -1
  52. data/lib/rubocop/cop/rails/where_equals.rb +28 -12
  53. data/lib/rubocop/cop/rails/where_not.rb +11 -6
  54. data/lib/rubocop/cop/rails/where_range.rb +89 -43
  55. data/lib/rubocop/cop/rails_cops.rb +4 -0
  56. data/lib/rubocop/rails/migration_file_skippable.rb +54 -0
  57. data/lib/rubocop/rails/schema_loader/schema.rb +1 -1
  58. data/lib/rubocop/rails/version.rb +1 -1
  59. data/lib/rubocop/rails.rb +0 -1
  60. data/lib/rubocop-rails.rb +3 -0
  61. metadata +11 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cad5e6d0c6f188b6ff87adb04fbfb73c7c574b901de45256eb7fa6e50b7a136d
4
- data.tar.gz: c8ab8f1d1c3284054af5847231dd996df24ee5441f638f95bc7e32420a3baf04
3
+ metadata.gz: 210e46df0db68aeef5761f3afeeb86d62a52015be55190d3e6e1fd65368721be
4
+ data.tar.gz: 7ff2ef268227cf39cb23b0479a8cf19c4ecc2acdf62c18f8e1766756e5965119
5
5
  SHA512:
6
- metadata.gz: ea0ebe45e988d115aa45d7bdba2b728b0583e1e2d996d43af424eb8abc17ced35b528741fcc47b8b258de52edd2fb7bc1c92dd609b49ddeeaaaf26bce123e783
7
- data.tar.gz: 7eb06cfce19f2a4ba78c6d571c65cf386e211b48ca2c9b5b9ca436ec9055e49f9737cbb3f9ce1b44ab432de87121295439effc59f093446cea88199bd08ba8a4
6
+ metadata.gz: c84c5690318920b054525a7fc21ca4e0f067a2093757f7ff8182d429587c94898ba1ea1160893fe29e9a8ef9427f6cc076f613f283b5b857da0529fea7654a47
7
+ data.tar.gz: ca501a5661bcebf2a6769a8cba620c637872e17023ac19fe8f6415b5ff4305d735ead0d24c7ffaee46f24c6cbeb466fcffeebdfb9cd96836211ab446643fc2a7
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
@@ -1,7 +1,7 @@
1
1
  # RuboCop Rails
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/rubocop-rails.svg)](https://badge.fury.io/rb/rubocop-rails)
4
- [![CircleCI](https://circleci.com/gh/rubocop/rubocop-rails.svg?style=svg)](https://circleci.com/gh/rubocop/rubocop-rails)
4
+ [![CI](https://github.com/rubocop/rubocop-rails/actions/workflows/test.yml/badge.svg)](https://github.com/rubocop/rubocop-rails/actions/workflows/test.yml)
5
5
 
6
6
  A [RuboCop](https://github.com/rubocop/rubocop) extension focused on enforcing Rails best practices and coding conventions.
7
7
 
@@ -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.'
@@ -424,6 +430,14 @@ Rails/EnumHash:
424
430
  Include:
425
431
  - app/models/**/*.rb
426
432
 
433
+ Rails/EnumSyntax:
434
+ Description: 'Use positional arguments over keyword arguments when defining enums.'
435
+ Enabled: pending
436
+ Severity: warning
437
+ VersionAdded: '2.26'
438
+ Include:
439
+ - app/models/**/*.rb
440
+
427
441
  Rails/EnumUniqueness:
428
442
  Description: 'Avoid duplicate integers in hash-syntax `enum` declaration.'
429
443
  Enabled: true
@@ -684,6 +698,15 @@ Rails/MigrationClassName:
684
698
  Include:
685
699
  - db/**/*.rb
686
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
+
687
710
  Rails/NegateInclude:
688
711
  Description: 'Prefer `collection.exclude?(obj)` over `!collection.include?(obj)`.'
689
712
  StyleGuide: 'https://rails.rubystyle.guide#exclude'
@@ -1018,8 +1041,9 @@ Rails/SkipsModelValidations:
1018
1041
  See reference for more information.
1019
1042
  Reference: 'https://guides.rubyonrails.org/active_record_validations.html#skipping-validations'
1020
1043
  Enabled: true
1044
+ Safe: false
1021
1045
  VersionAdded: '0.47'
1022
- VersionChanged: '2.7'
1046
+ VersionChanged: '2.25'
1023
1047
  ForbiddenMethods:
1024
1048
  - decrement!
1025
1049
  - decrement_counter
@@ -1057,6 +1081,15 @@ Rails/StripHeredoc:
1057
1081
  Enabled: pending
1058
1082
  VersionAdded: '2.15'
1059
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
+
1060
1093
  Rails/TableNameAssignment:
1061
1094
  Description: >-
1062
1095
  Do not use `self.table_name =`. Use Inflections or `table_name_prefix` instead.
@@ -1184,12 +1217,12 @@ Rails/Validation:
1184
1217
  - app/models/**/*.rb
1185
1218
 
1186
1219
  Rails/WhereEquals:
1187
- Description: 'Pass conditions to `where` as a hash instead of manually constructing SQL.'
1220
+ Description: 'Pass conditions to `where` and `where.not` as a hash instead of manually constructing SQL.'
1188
1221
  StyleGuide: 'https://rails.rubystyle.guide/#hash-conditions'
1189
1222
  Enabled: 'pending'
1190
1223
  SafeAutoCorrect: false
1191
1224
  VersionAdded: '2.9'
1192
- VersionChanged: '2.10'
1225
+ VersionChanged: '2.26'
1193
1226
 
1194
1227
  Rails/WhereExists:
1195
1228
  Description: 'Prefer `exists?(...)` over `where(...).exists?`.'
@@ -1226,12 +1259,17 @@ Rails/WhereRange:
1226
1259
  Description: 'Use ranges in `where` instead of manually constructing SQL.'
1227
1260
  StyleGuide: 'https://rails.rubystyle.guide/#where-ranges'
1228
1261
  Enabled: pending
1262
+ SafeAutoCorrect: false
1229
1263
  VersionAdded: '2.25'
1230
1264
 
1231
1265
  # Accept `redirect_to(...) and return` and similar cases.
1232
1266
  Style/AndOr:
1233
1267
  EnforcedStyle: conditionals
1234
1268
 
1269
+ Style/CollectionCompact:
1270
+ AllowedReceivers:
1271
+ - params
1272
+
1235
1273
  Style/FormatStringToken:
1236
1274
  AllowedMethods:
1237
1275
  - redirect
@@ -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
@@ -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.
@@ -113,8 +114,10 @@ module RuboCop
113
114
  MYSQL_COMBINABLE_ALTER_METHODS = %i[rename_column add_index remove_index].freeze
114
115
 
115
116
  POSTGRESQL_COMBINABLE_TRANSFORMATIONS = %i[change_default].freeze
117
+ POSTGRESQL_COMBINABLE_TRANSFORMATIONS_SINCE_6_1 = %i[change_null].freeze
116
118
 
117
119
  POSTGRESQL_COMBINABLE_ALTER_METHODS = %i[change_column_default].freeze
120
+ POSTGRESQL_COMBINABLE_ALTER_METHODS_SINCE_6_1 = %i[change_column_null].freeze
118
121
 
119
122
  def on_def(node)
120
123
  return unless support_bulk_alter?
@@ -138,9 +141,9 @@ module RuboCop
138
141
  return unless support_bulk_alter?
139
142
  return unless node.command?(:change_table)
140
143
  return if include_bulk_options?(node)
141
- return unless node.block_node
144
+ return unless (body = node.block_node&.body)
142
145
 
143
- send_nodes = send_nodes_from_change_table_block(node.block_node.body)
146
+ send_nodes = send_nodes_from_change_table_block(body)
144
147
 
145
148
  add_offense_for_change_table(node) if count_transformations(send_nodes) > 1
146
149
  end
@@ -196,7 +199,9 @@ module RuboCop
196
199
  when MYSQL
197
200
  COMBINABLE_ALTER_METHODS + MYSQL_COMBINABLE_ALTER_METHODS
198
201
  when POSTGRESQL
199
- COMBINABLE_ALTER_METHODS + POSTGRESQL_COMBINABLE_ALTER_METHODS
202
+ result = COMBINABLE_ALTER_METHODS + POSTGRESQL_COMBINABLE_ALTER_METHODS
203
+ result += POSTGRESQL_COMBINABLE_ALTER_METHODS_SINCE_6_1 if target_rails_version >= 6.1
204
+ result
200
205
  end
201
206
  end
202
207
 
@@ -205,7 +210,9 @@ module RuboCop
205
210
  when MYSQL
206
211
  COMBINABLE_TRANSFORMATIONS + MYSQL_COMBINABLE_TRANSFORMATIONS
207
212
  when POSTGRESQL
208
- COMBINABLE_TRANSFORMATIONS + POSTGRESQL_COMBINABLE_TRANSFORMATIONS
213
+ result = COMBINABLE_TRANSFORMATIONS + POSTGRESQL_COMBINABLE_TRANSFORMATIONS
214
+ result += POSTGRESQL_COMBINABLE_TRANSFORMATIONS_SINCE_6_1 if target_rails_version >= 6.1
215
+ result
209
216
  end
210
217
  end
211
218
 
@@ -16,7 +16,6 @@ module RuboCop
16
16
  # And `compact_blank!` has different implementations for `Array`, `Hash`, and
17
17
  # `ActionController::Parameters`.
18
18
  # `Array#compact_blank!`, `Hash#compact_blank!` are equivalent to `delete_if(&:blank?)`.
19
- # `ActionController::Parameters#compact_blank!` is equivalent to `reject!(&:blank?)`.
20
19
  # If the cop makes a mistake, autocorrected code may get unexpected behavior.
21
20
  #
22
21
  # @example
@@ -24,6 +23,10 @@ module RuboCop
24
23
  # # bad
25
24
  # collection.reject(&:blank?)
26
25
  # collection.reject { |_k, v| v.blank? }
26
+ # collection.select(&:present?)
27
+ # collection.select { |_k, v| v.present? }
28
+ # collection.filter(&:present?)
29
+ # collection.filter { |_k, v| v.present? }
27
30
  #
28
31
  # # good
29
32
  # collection.compact_blank
@@ -31,8 +34,8 @@ module RuboCop
31
34
  # # bad
32
35
  # collection.delete_if(&:blank?) # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
33
36
  # collection.delete_if { |_k, v| v.blank? } # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
34
- # collection.reject!(&:blank?) # Same behavior as `ActionController::Parameters#compact_blank!`
35
- # collection.reject! { |_k, v| v.blank? } # Same behavior as `ActionController::Parameters#compact_blank!`
37
+ # collection.keep_if(&:present?) # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
38
+ # collection.keep_if { |_k, v| v.present? } # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
36
39
  #
37
40
  # # good
38
41
  # collection.compact_blank!
@@ -43,25 +46,41 @@ module RuboCop
43
46
  extend TargetRailsVersion
44
47
 
45
48
  MSG = 'Use `%<preferred_method>s` instead.'
46
- RESTRICT_ON_SEND = %i[reject delete_if reject!].freeze
49
+ RESTRICT_ON_SEND = %i[reject delete_if select filter keep_if].freeze
50
+ DESTRUCTIVE_METHODS = %i[delete_if keep_if].freeze
47
51
 
48
52
  minimum_target_rails_version 6.1
49
53
 
50
54
  def_node_matcher :reject_with_block?, <<~PATTERN
51
55
  (block
52
- (send _ {:reject :delete_if :reject!})
56
+ (send _ {:reject :delete_if})
53
57
  $(args ...)
54
58
  (send
55
59
  $(lvar _) :blank?))
56
60
  PATTERN
57
61
 
58
62
  def_node_matcher :reject_with_block_pass?, <<~PATTERN
59
- (send _ {:reject :delete_if :reject!}
63
+ (send _ {:reject :delete_if}
60
64
  (block_pass
61
65
  (sym :blank?)))
62
66
  PATTERN
63
67
 
68
+ def_node_matcher :select_with_block?, <<~PATTERN
69
+ (block
70
+ (send _ {:select :filter :keep_if})
71
+ $(args ...)
72
+ (send
73
+ $(lvar _) :present?))
74
+ PATTERN
75
+
76
+ def_node_matcher :select_with_block_pass?, <<~PATTERN
77
+ (send _ {:select :filter :keep_if}
78
+ (block-pass
79
+ (sym :present?)))
80
+ PATTERN
81
+
64
82
  def on_send(node)
83
+ return if target_ruby_version < 2.6 && node.method?(:filter)
65
84
  return unless bad_method?(node)
66
85
 
67
86
  range = offense_range(node)
@@ -75,8 +94,10 @@ module RuboCop
75
94
 
76
95
  def bad_method?(node)
77
96
  return true if reject_with_block_pass?(node)
97
+ return true if select_with_block_pass?(node)
78
98
 
79
- if (arguments, receiver_in_block = reject_with_block?(node.parent))
99
+ arguments, receiver_in_block = reject_with_block?(node.parent) || select_with_block?(node.parent)
100
+ if arguments
80
101
  return use_single_value_block_argument?(arguments, receiver_in_block) ||
81
102
  use_hash_value_block_argument?(arguments, receiver_in_block)
82
103
  end
@@ -103,7 +124,7 @@ module RuboCop
103
124
  end
104
125
 
105
126
  def preferred_method(node)
106
- node.method?(:reject) ? 'compact_blank' : 'compact_blank!'
127
+ DESTRUCTIVE_METHODS.include?(node.method_name) ? 'compact_blank!' : 'compact_blank'
107
128
  end
108
129
  end
109
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
@@ -12,10 +12,10 @@ module RuboCop
12
12
  # The cop also reports warnings when you are using `to_time` method,
13
13
  # because it doesn't know about Rails time zone either.
14
14
  #
15
- # Two styles are supported for this cop. When `EnforcedStyle` is 'strict'
15
+ # Two styles are supported for this cop. When `EnforcedStyle` is `strict`
16
16
  # then the Date methods `today`, `current`, `yesterday`, and `tomorrow`
17
17
  # are prohibited and the usage of both `to_time`
18
- # and 'to_time_in_current_zone' are reported as warning.
18
+ # and `to_time_in_current_zone` are reported as warning.
19
19
  #
20
20
  # When `EnforcedStyle` is `flexible` then only `Date.today` is prohibited.
21
21
  #
@@ -12,6 +12,12 @@ module RuboCop
12
12
  #
13
13
  # @example
14
14
  # # bad
15
+ # enum :status, [:active, :archived]
16
+ #
17
+ # # good
18
+ # enum :status, { active: 0, archived: 1 }
19
+ #
20
+ # # bad
15
21
  # enum status: [:active, :archived]
16
22
  #
17
23
  # # good
@@ -23,7 +29,11 @@ module RuboCop
23
29
  MSG = 'Enum defined as an array found in `%<enum>s` enum declaration. Use hash syntax instead.'
24
30
  RESTRICT_ON_SEND = %i[enum].freeze
25
31
 
26
- def_node_matcher :enum?, <<~PATTERN
32
+ def_node_matcher :enum_with_array?, <<~PATTERN
33
+ (send nil? :enum $_ ${array} ...)
34
+ PATTERN
35
+
36
+ def_node_matcher :enum_with_old_syntax?, <<~PATTERN
27
37
  (send nil? :enum (hash $...))
28
38
  PATTERN
29
39
 
@@ -32,17 +42,19 @@ module RuboCop
32
42
  PATTERN
33
43
 
34
44
  def on_send(node)
35
- enum?(node) do |pairs|
45
+ target_rails_version >= 7.0 && enum_with_array?(node) do |key, array|
46
+ add_offense(array, message: message(key)) do |corrector|
47
+ corrector.replace(array, build_hash(array))
48
+ end
49
+ end
50
+
51
+ enum_with_old_syntax?(node) do |pairs|
36
52
  pairs.each do |pair|
37
53
  key, array = array_pair?(pair)
38
54
  next unless key
39
55
 
40
- add_offense(array, message: format(MSG, enum: enum_name(key))) do |corrector|
41
- hash = array.children.each_with_index.map do |elem, index|
42
- "#{source(elem)} => #{index}"
43
- end.join(', ')
44
-
45
- corrector.replace(array, "{#{hash}}")
56
+ add_offense(array, message: message(key)) do |corrector|
57
+ corrector.replace(array, build_hash(array))
46
58
  end
47
59
  end
48
60
  end
@@ -50,6 +62,10 @@ module RuboCop
50
62
 
51
63
  private
52
64
 
65
+ def message(key)
66
+ format(MSG, enum: enum_name(key))
67
+ end
68
+
53
69
  def enum_name(key)
54
70
  case key.type
55
71
  when :sym, :str
@@ -69,6 +85,13 @@ module RuboCop
69
85
  elem.source
70
86
  end
71
87
  end
88
+
89
+ def build_hash(array)
90
+ hash = array.children.each_with_index.map do |elem, index|
91
+ "#{source(elem)} => #{index}"
92
+ end.join(', ')
93
+ "{#{hash}}"
94
+ end
72
95
  end
73
96
  end
74
97
  end