rubocop-rails 2.25.0 → 2.29.0

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