rubocop-rails 2.28.0 → 2.29.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 758b691d39a36d50333d038d5efc0615f3da32aa8b47f0a465f684586b15463c
4
- data.tar.gz: 8cf337b9a94306e55af10e5c7f0ee1e607fe22b27ca02cde32ece30e0c8c9bb5
3
+ metadata.gz: 210e46df0db68aeef5761f3afeeb86d62a52015be55190d3e6e1fd65368721be
4
+ data.tar.gz: 7ff2ef268227cf39cb23b0479a8cf19c4ecc2acdf62c18f8e1766756e5965119
5
5
  SHA512:
6
- metadata.gz: b5841dbc4ae81e23e5eac1921e5752204e0c0dea5d17e0edd894329bd8d8764d0594b44a045f0d4f722d8b6927037f1b8817a1d29f444c75555ecd94feb8180d
7
- data.tar.gz: 2adfb7937e87109d54e8fe596bc653528a62cfe87cebe7554ee1865a07daf5c3169fb7387bf6dacc59e71ee479ddd718c2775f2a1df573f5383b1f10fc27fd42
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
@@ -90,7 +90,7 @@ When `MigratedSchemaVersion: '20241225000000'` is set, migration files lower tha
90
90
  For example, to ignore db/migrate/20241225000000_create_articles.rb and earlier migrations you would configure it the following way:
91
91
 
92
92
  ```yaml
93
- AllCops
93
+ AllCops:
94
94
  MigratedSchemaVersion: '20241225000000'
95
95
  ```
96
96
 
data/config/default.yml CHANGED
@@ -698,6 +698,15 @@ Rails/MigrationClassName:
698
698
  Include:
699
699
  - db/**/*.rb
700
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
+
701
710
  Rails/NegateInclude:
702
711
  Description: 'Prefer `collection.exclude?(obj)` over `!collection.include?(obj)`.'
703
712
  StyleGuide: 'https://rails.rubystyle.guide#exclude'
@@ -1072,6 +1081,15 @@ Rails/StripHeredoc:
1072
1081
  Enabled: pending
1073
1082
  VersionAdded: '2.15'
1074
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
+
1075
1093
  Rails/TableNameAssignment:
1076
1094
  Description: >-
1077
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
@@ -21,35 +21,6 @@ module RuboCop
21
21
  migration_class?(class_node)
22
22
  end
23
23
  end
24
-
25
- # rubocop:disable Style/DocumentDynamicEvalDefinition
26
- %i[on_send on_csend on_block on_numblock on_class].each do |method|
27
- class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
28
- def #{method}(node)
29
- return if already_migrated_file?
30
-
31
- super if method(__method__).super_method
32
- end
33
- RUBY
34
- end
35
- # rubocop:enable Style/DocumentDynamicEvalDefinition
36
-
37
- private
38
-
39
- def already_migrated_file?
40
- return false unless migrated_schema_version
41
-
42
- match_data = File.basename(processed_source.file_path).match(/(?<timestamp>\d{14})/)
43
- schema_version = match_data['timestamp'] if match_data
44
-
45
- return false unless schema_version
46
-
47
- schema_version <= migrated_schema_version.to_s # Ignore applied migration files.
48
- end
49
-
50
- def migrated_schema_version
51
- config.for_all_cops.fetch('MigratedSchemaVersion', nil)
52
- end
53
24
  end
54
25
  end
55
26
  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
@@ -19,7 +19,7 @@ module RuboCop
19
19
  class AddColumnIndex < Base
20
20
  extend AutoCorrector
21
21
  include RangeHelp
22
- prepend MigrationsHelper
22
+ include MigrationsHelper
23
23
 
24
24
  MSG = '`add_column` does not accept an `index` key, use `add_index` instead.'
25
25
  RESTRICT_ON_SEND = %i[add_column].freeze
@@ -65,7 +65,7 @@ module RuboCop
65
65
  # end
66
66
  class BulkChangeTable < Base
67
67
  include DatabaseTypeResolvable
68
- prepend MigrationsHelper
68
+ include MigrationsHelper
69
69
 
70
70
  MSG_FOR_CHANGE_TABLE = <<~MSG.chomp
71
71
  You can combine alter queries using `bulk: true` options.
@@ -14,7 +14,7 @@ module RuboCop
14
14
  # # good
15
15
  # add_column :users, :saved
16
16
  class DangerousColumnNames < Base # rubocop:disable Metrics/ClassLength
17
- prepend MigrationsHelper
17
+ include MigrationsHelper
18
18
 
19
19
  COLUMN_TYPE_METHOD_NAMES = %i[
20
20
  bigint
@@ -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)
@@ -29,18 +29,28 @@ module RuboCop
29
29
  PATTERN
30
30
 
31
31
  def_node_matcher :on_bad_to_h, <<~PATTERN
32
- (block
33
- (call _ :to_h)
34
- (args (arg $_el))
35
- (array $_ (lvar _el)))
32
+ {
33
+ (block
34
+ (call _ :to_h)
35
+ (args (arg $_el))
36
+ (array $_ (lvar _el)))
37
+ (numblock
38
+ (call _ :to_h) $1
39
+ (array $_ (lvar :_1)))
40
+ }
36
41
  PATTERN
37
42
 
38
43
  def_node_matcher :on_bad_map_to_h, <<~PATTERN
39
44
  (call
40
- (block
41
- (call _ {:map :collect})
42
- (args (arg $_el))
43
- (array $_ (lvar _el)))
45
+ {
46
+ (block
47
+ (call _ {:map :collect})
48
+ (args (arg $_el))
49
+ (array $_ (lvar _el)))
50
+ (numblock
51
+ (call _ {:map :collect}) $1
52
+ (array $_ (lvar :_1)))
53
+ }
44
54
  :to_h)
45
55
  PATTERN
46
56
 
@@ -48,10 +58,16 @@ module RuboCop
48
58
  (send
49
59
  (const {nil? cbase} :Hash)
50
60
  :[]
51
- (block
52
- (call _ {:map :collect})
53
- (args (arg $_el))
54
- (array $_ (lvar _el))))
61
+ {
62
+ (block
63
+ (call _ {:map :collect})
64
+ (args (arg $_el))
65
+ (array $_ (lvar _el)))
66
+ (numblock
67
+ (call _ {:map :collect}) $1
68
+ (array $_ (lvar :_1)))
69
+ }
70
+ )
55
71
  PATTERN
56
72
 
57
73
  private
@@ -32,18 +32,28 @@ module RuboCop
32
32
  PATTERN
33
33
 
34
34
  def_node_matcher :on_bad_to_h, <<~PATTERN
35
- (block
36
- (call _ :to_h)
37
- (args (arg $_el))
38
- (array (lvar _el) $_))
35
+ {
36
+ (block
37
+ (call _ :to_h)
38
+ (args (arg $_el))
39
+ (array (lvar _el) $_))
40
+ (numblock
41
+ (call _ :to_h) $1
42
+ (array (lvar :_1) $_))
43
+ }
39
44
  PATTERN
40
45
 
41
46
  def_node_matcher :on_bad_map_to_h, <<~PATTERN
42
47
  (call
43
- (block
44
- (call _ {:map :collect})
45
- (args (arg $_el))
46
- (array (lvar _el) $_))
48
+ {
49
+ (block
50
+ (call _ {:map :collect})
51
+ (args (arg $_el))
52
+ (array (lvar _el) $_))
53
+ (numblock
54
+ (call _ {:map :collect}) $1
55
+ (array (lvar :_1) $_))
56
+ }
47
57
  :to_h)
48
58
  PATTERN
49
59
 
@@ -51,10 +61,16 @@ module RuboCop
51
61
  (send
52
62
  (const {nil? cbase} :Hash)
53
63
  :[]
54
- (block
55
- (call _ {:map :collect})
56
- (args (arg $_el))
57
- (array (lvar _el) $_)))
64
+ {
65
+ (block
66
+ (call _ {:map :collect})
67
+ (args (arg $_el))
68
+ (array (lvar _el) $_))
69
+ (numblock
70
+ (call _ {:map :collect}) $1
71
+ (array (lvar :_1) $_))
72
+ }
73
+ )
58
74
  PATTERN
59
75
 
60
76
  private
@@ -21,11 +21,11 @@ module RuboCop
21
21
  # match 'photos/:id', to: 'photos#show', via: :all
22
22
  #
23
23
  class MatchRoute < Base
24
+ include RoutesHelper
24
25
  extend AutoCorrector
25
26
 
26
27
  MSG = 'Use `%<http_method>s` instead of `match` to define a route.'
27
28
  RESTRICT_ON_SEND = %i[match].freeze
28
- HTTP_METHODS = %i[get post put patch delete].freeze
29
29
 
30
30
  def_node_matcher :match_method_call?, <<~PATTERN
31
31
  (send nil? :match $_ $(hash ...) ?)
@@ -60,14 +60,6 @@ module RuboCop
60
60
  end
61
61
  end
62
62
 
63
- def_node_matcher :routes_draw?, <<~PATTERN
64
- (send (send _ :routes) :draw)
65
- PATTERN
66
-
67
- def within_routes?(node)
68
- node.each_ancestor(:block).any? { |a| routes_draw?(a.send_node) }
69
- end
70
-
71
63
  def extract_via(node)
72
64
  via_pair = via_pair(node)
73
65
  return %i[get] unless via_pair
@@ -20,7 +20,7 @@ module RuboCop
20
20
  #
21
21
  class MigrationClassName < Base
22
22
  extend AutoCorrector
23
- prepend MigrationsHelper
23
+ include MigrationsHelper
24
24
 
25
25
  MSG = 'Replace with `%<camelized_basename>s` that matches the file name.'
26
26
 
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Checks for mapping a route with multiple paths, which is deprecated and will be removed in Rails 8.1.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # get '/users', '/other_path', to: 'users#index'
12
+ #
13
+ # # good
14
+ # get '/users', to: 'users#index'
15
+ # get '/other_path', to: 'users#index'
16
+ #
17
+ class MultipleRoutePaths < Base
18
+ include RoutesHelper
19
+ extend AutoCorrector
20
+
21
+ MSG = 'Use separate routes instead of combining multiple route paths in a single route.'
22
+ RESTRICT_ON_SEND = HTTP_METHODS
23
+
24
+ IGNORED_ARGUMENT_TYPES = %i[array hash].freeze
25
+
26
+ def on_send(node)
27
+ return unless within_routes?(node)
28
+
29
+ route_paths = node.arguments.reject { |argument| IGNORED_ARGUMENT_TYPES.include?(argument.type) }
30
+ return if route_paths.count < 2
31
+
32
+ add_offense(node) do |corrector|
33
+ corrector.replace(node, migrate_to_multiple_routes(node, route_paths))
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def migrate_to_multiple_routes(node, route_paths)
40
+ rest = route_paths.last.source_range.end.join(node.source_range.end).source
41
+ indentation = ' ' * node.source_range.column
42
+
43
+ route_paths.map do |route_path|
44
+ "#{node.method_name} #{route_path.source}#{rest}"
45
+ end.join("\n#{indentation}")
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -41,11 +41,14 @@ module RuboCop
41
41
  # change_column_null :products, :category_id, false
42
42
  class NotNullColumn < Base
43
43
  include DatabaseTypeResolvable
44
- prepend MigrationsHelper
44
+ include MigrationsHelper
45
45
 
46
46
  MSG = 'Do not add a NOT NULL column without a default value.'
47
47
  RESTRICT_ON_SEND = %i[add_column add_reference].freeze
48
48
 
49
+ VIRTUAL_TYPE_VALUES = [:virtual, 'virtual'].freeze
50
+ TEXT_TYPE_VALUES = [:text, 'text'].freeze
51
+
49
52
  def_node_matcher :add_not_null_column?, <<~PATTERN
50
53
  (send nil? :add_column _ _ $_ (hash $...))
51
54
  PATTERN
@@ -92,8 +95,8 @@ module RuboCop
92
95
 
93
96
  def check_column(type, pairs)
94
97
  if type.respond_to?(:value)
95
- return if type.value == :virtual || type.value == 'virtual'
96
- return if (type.value == :text || type.value == 'text') && database == MYSQL
98
+ return if VIRTUAL_TYPE_VALUES.include?(type.value)
99
+ return if TEXT_TYPE_VALUES.include?(type.value) && database == MYSQL
97
100
  end
98
101
 
99
102
  check_pairs(pairs)
@@ -151,7 +151,7 @@ module RuboCop
151
151
  # remove_index :users, column: :email
152
152
  # end
153
153
  class ReversibleMigration < Base
154
- prepend MigrationsHelper
154
+ include MigrationsHelper
155
155
 
156
156
  MSG = '%<action>s is not reversible.'
157
157
 
@@ -215,8 +215,10 @@ module RuboCop
215
215
  end
216
216
 
217
217
  def check_drop_table_node(node)
218
+ return unless (last_argument = node.last_argument)
219
+
218
220
  drop_table_call(node) do
219
- unless node.parent.block_type? || node.last_argument.block_pass_type?
221
+ unless node.parent.block_type? || last_argument.block_pass_type?
220
222
  add_offense(node, message: format(MSG, action: 'drop_table(without block)'))
221
223
  end
222
224
  end
@@ -43,7 +43,7 @@ module RuboCop
43
43
  # end
44
44
  # end
45
45
  class ReversibleMigrationMethodDefinition < Base
46
- prepend MigrationsHelper
46
+ include MigrationsHelper
47
47
 
48
48
  MSG = 'Migrations must contain either a `change` method, or both an `up` and a `down` method.'
49
49
 
@@ -23,7 +23,7 @@ module RuboCop
23
23
  #
24
24
  class SchemaComment < Base
25
25
  include ActiveRecordMigrationsHelper
26
- prepend MigrationsHelper
26
+ include MigrationsHelper
27
27
 
28
28
  COLUMN_MSG = 'New database column without `comment`.'
29
29
  TABLE_MSG = 'New database table without `comment`.'
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Enforces the use of `ActionController::Parameters#expect` as a method for strong parameter handling.
7
+ #
8
+ # @safety
9
+ # This cop's autocorrection is considered unsafe because there are cases where the HTTP status may change
10
+ # from 500 to 400 when handling invalid parameters. This change, however, reflects an intentional
11
+ # incompatibility introduced for valid reasons by the `expect` method, which aligns better with
12
+ # strong parameter conventions.
13
+ #
14
+ # @example
15
+ #
16
+ # # bad
17
+ # params.require(:user).permit(:name, :age)
18
+ # params.permit(user: [:name, :age]).require(:user)
19
+ #
20
+ # # good
21
+ # params.expect(user: [:name, :age])
22
+ #
23
+ class StrongParametersExpect < Base
24
+ extend AutoCorrector
25
+ extend TargetRailsVersion
26
+
27
+ MSG = 'Use `%<prefer>s` instead.'
28
+ RESTRICT_ON_SEND = %i[require permit].freeze
29
+
30
+ minimum_target_rails_version 8.0
31
+
32
+ def_node_matcher :params_require_permit, <<~PATTERN
33
+ $(call
34
+ $(call
35
+ (send nil? :params) :require _) :permit ...)
36
+ PATTERN
37
+
38
+ def_node_matcher :params_permit_require, <<~PATTERN
39
+ $(call
40
+ $(call
41
+ (send nil? :params) :permit (hash (pair _require_param_name _ )))
42
+ :require _require_param_name)
43
+ PATTERN
44
+
45
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
46
+ def on_send(node)
47
+ return if part_of_ignored_node?(node)
48
+
49
+ if (permit_method, require_method = params_require_permit(node))
50
+ range = offense_range(require_method, node)
51
+ prefer = expect_method(require_method, permit_method)
52
+ replace_argument = true
53
+ elsif (require_method, permit_method = params_permit_require(node))
54
+ range = offense_range(permit_method, node)
55
+ prefer = "expect(#{permit_method.arguments.map(&:source).join(', ')})"
56
+ replace_argument = false
57
+ else
58
+ return
59
+ end
60
+
61
+ add_offense(range, message: format(MSG, prefer: prefer)) do |corrector|
62
+ corrector.remove(require_method.loc.dot.join(require_method.source_range.end))
63
+ corrector.replace(permit_method.loc.selector, 'expect')
64
+ if replace_argument
65
+ corrector.insert_before(permit_method.first_argument, "#{require_key(require_method)}[")
66
+ corrector.insert_after(permit_method.last_argument, ']')
67
+ end
68
+ end
69
+
70
+ ignore_node(node)
71
+ end
72
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
73
+ alias on_csend on_send
74
+
75
+ private
76
+
77
+ def offense_range(method_node, node)
78
+ method_node.loc.selector.join(node.source_range.end)
79
+ end
80
+
81
+ def expect_method(require_method, permit_method)
82
+ require_key = require_key(require_method)
83
+ permit_args = permit_method.arguments.map(&:source).join(', ')
84
+
85
+ arguments = "#{require_key}[#{permit_args}]"
86
+
87
+ "expect(#{arguments})"
88
+ end
89
+
90
+ def require_key(require_method)
91
+ if (first_argument = require_method.first_argument).respond_to?(:value)
92
+ require_arg = first_argument.value
93
+ separator = ': '
94
+ else
95
+ require_arg = first_argument.source
96
+ separator = ' => '
97
+ end
98
+
99
+ "#{require_arg}#{separator}"
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -18,7 +18,7 @@ module RuboCop
18
18
  # t.boolean :active, default: true, null: false
19
19
  #
20
20
  class ThreeStateBooleanColumn < Base
21
- prepend MigrationsHelper
21
+ include MigrationsHelper
22
22
 
23
23
  MSG = 'Boolean columns should always have a default value and a `NOT NULL` constraint.'
24
24
  RESTRICT_ON_SEND = %i[add_column column boolean].freeze
@@ -97,11 +97,9 @@ module RuboCop
97
97
  end
98
98
 
99
99
  def autocorrect_time_new(node, corrector)
100
- if node.arguments?
101
- corrector.replace(node.loc.selector, 'local')
102
- else
103
- corrector.replace(node.loc.selector, 'now')
104
- end
100
+ replacement = replacement(node)
101
+
102
+ corrector.replace(node.loc.selector, replacement)
105
103
  end
106
104
 
107
105
  # remove redundant `.in_time_zone` from `Time.zone.now.in_time_zone`
@@ -183,7 +181,7 @@ module RuboCop
183
181
 
184
182
  def safe_method(method_name, node)
185
183
  if %w[new current].include?(method_name)
186
- node.arguments? ? 'local' : 'now'
184
+ replacement(node)
187
185
  else
188
186
  method_name
189
187
  end
@@ -259,6 +257,12 @@ module RuboCop
259
257
  pair.key.sym_type? && pair.key.value == :in && !pair.value.nil_type?
260
258
  end
261
259
  end
260
+
261
+ def replacement(node)
262
+ return 'now' unless node.arguments?
263
+
264
+ node.first_argument.str_type? ? 'parse' : 'local'
265
+ end
262
266
  end
263
267
  end
264
268
  end
@@ -7,9 +7,11 @@ require_relative 'mixin/database_type_resolvable'
7
7
  require_relative 'mixin/enforce_superclass'
8
8
  require_relative 'mixin/index_method'
9
9
  require_relative 'mixin/migrations_helper'
10
+ require_relative 'mixin/routes_helper'
10
11
  require_relative 'mixin/target_rails_version'
11
12
 
12
13
  require_relative 'rails/action_controller_flash_before_render'
14
+ require_relative 'rails/strong_parameters_expect'
13
15
  require_relative 'rails/action_controller_test_case'
14
16
  require_relative 'rails/action_filter'
15
17
  require_relative 'rails/action_order'
@@ -77,6 +79,7 @@ require_relative 'rails/link_to_blank'
77
79
  require_relative 'rails/mailer_name'
78
80
  require_relative 'rails/match_route'
79
81
  require_relative 'rails/migration_class_name'
82
+ require_relative 'rails/multiple_route_paths'
80
83
  require_relative 'rails/negate_include'
81
84
  require_relative 'rails/not_null_column'
82
85
  require_relative 'rails/order_by_id'
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Rails
5
+ # This module allows cops to detect and ignore files that have already been migrated
6
+ # by leveraging the `AllCops: MigratedSchemaVersion` configuration.
7
+ #
8
+ # [source,yaml]
9
+ # -----
10
+ # AllCops:
11
+ # MigratedSchemaVersion: '20241225000000'
12
+ # -----
13
+ #
14
+ # When applied to cops, it overrides the `add_global_offense` and `add_offense` methods,
15
+ # ensuring that cops skip processing if the file is determined to be a migrated file
16
+ # based on the schema version.
17
+ #
18
+ # @api private
19
+ module MigrationFileSkippable
20
+ def add_global_offense(message = nil, severity: nil)
21
+ return if already_migrated_file?
22
+
23
+ super if method(__method__).super_method
24
+ end
25
+
26
+ def add_offense(node_or_range, message: nil, severity: nil, &block)
27
+ return if already_migrated_file?
28
+
29
+ super if method(__method__).super_method
30
+ end
31
+
32
+ def self.apply_to_cops!
33
+ RuboCop::Cop::Registry.all.each { |cop| cop.prepend(MigrationFileSkippable) }
34
+ end
35
+
36
+ private
37
+
38
+ def already_migrated_file?
39
+ return false unless migrated_schema_version
40
+
41
+ match_data = File.basename(processed_source.file_path).match(/(?<timestamp>\d{14})/)
42
+ schema_version = match_data['timestamp'] if match_data
43
+
44
+ return false unless schema_version
45
+
46
+ schema_version <= migrated_schema_version.to_s # Ignore applied migration files.
47
+ end
48
+
49
+ def migrated_schema_version
50
+ @migrated_schema_version ||= config.for_all_cops.fetch('MigratedSchemaVersion', nil)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Rails
5
5
  # This module holds the RuboCop Rails version information.
6
6
  module Version
7
- STRING = '2.28.0'
7
+ STRING = '2.29.0'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
data/lib/rubocop/rails.rb CHANGED
@@ -5,7 +5,6 @@ module RuboCop
5
5
  module Rails
6
6
  PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze
7
7
  CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze
8
- CONFIG = YAML.safe_load(CONFIG_DEFAULT.read, permitted_classes: [Regexp, Symbol]).freeze
9
8
 
10
9
  private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
11
10
 
data/lib/rubocop-rails.rb CHANGED
@@ -15,6 +15,9 @@ RuboCop::Rails::Inject.defaults!
15
15
 
16
16
  require_relative 'rubocop/cop/rails_cops'
17
17
 
18
+ require_relative 'rubocop/rails/migration_file_skippable'
19
+ RuboCop::Rails::MigrationFileSkippable.apply_to_cops!
20
+
18
21
  RuboCop::Cop::Style::HashExcept.minimum_target_ruby_version(2.0)
19
22
 
20
23
  RuboCop::Cop::Style::InverseMethods.singleton_class.prepend(
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.28.0
4
+ version: 2.29.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -9,7 +9,7 @@ authors:
9
9
  - Yuji Nakayama
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-12-25 00:00:00.000000000 Z
12
+ date: 2025-01-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -101,6 +101,7 @@ files:
101
101
  - lib/rubocop/cop/mixin/enforce_superclass.rb
102
102
  - lib/rubocop/cop/mixin/index_method.rb
103
103
  - lib/rubocop/cop/mixin/migrations_helper.rb
104
+ - lib/rubocop/cop/mixin/routes_helper.rb
104
105
  - lib/rubocop/cop/mixin/target_rails_version.rb
105
106
  - lib/rubocop/cop/rails/action_controller_flash_before_render.rb
106
107
  - lib/rubocop/cop/rails/action_controller_test_case.rb
@@ -170,6 +171,7 @@ files:
170
171
  - lib/rubocop/cop/rails/mailer_name.rb
171
172
  - lib/rubocop/cop/rails/match_route.rb
172
173
  - lib/rubocop/cop/rails/migration_class_name.rb
174
+ - lib/rubocop/cop/rails/multiple_route_paths.rb
173
175
  - lib/rubocop/cop/rails/negate_include.rb
174
176
  - lib/rubocop/cop/rails/not_null_column.rb
175
177
  - lib/rubocop/cop/rails/order_by_id.rb
@@ -213,6 +215,7 @@ files:
213
215
  - lib/rubocop/cop/rails/skips_model_validations.rb
214
216
  - lib/rubocop/cop/rails/squished_sql_heredocs.rb
215
217
  - lib/rubocop/cop/rails/strip_heredoc.rb
218
+ - lib/rubocop/cop/rails/strong_parameters_expect.rb
216
219
  - lib/rubocop/cop/rails/table_name_assignment.rb
217
220
  - lib/rubocop/cop/rails/three_state_boolean_column.rb
218
221
  - lib/rubocop/cop/rails/time_zone.rb
@@ -236,6 +239,7 @@ files:
236
239
  - lib/rubocop/cop/rails_cops.rb
237
240
  - lib/rubocop/rails.rb
238
241
  - lib/rubocop/rails/inject.rb
242
+ - lib/rubocop/rails/migration_file_skippable.rb
239
243
  - lib/rubocop/rails/schema_loader.rb
240
244
  - lib/rubocop/rails/schema_loader/schema.rb
241
245
  - lib/rubocop/rails/version.rb
@@ -246,7 +250,7 @@ metadata:
246
250
  homepage_uri: https://docs.rubocop.org/rubocop-rails/
247
251
  changelog_uri: https://github.com/rubocop/rubocop-rails/blob/master/CHANGELOG.md
248
252
  source_code_uri: https://github.com/rubocop/rubocop-rails/
249
- documentation_uri: https://docs.rubocop.org/rubocop-rails/2.28/
253
+ documentation_uri: https://docs.rubocop.org/rubocop-rails/2.29/
250
254
  bug_tracker_uri: https://github.com/rubocop/rubocop-rails/issues
251
255
  rubygems_mfa_required: 'true'
252
256
  rdoc_options: []
@@ -263,7 +267,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
263
267
  - !ruby/object:Gem::Version
264
268
  version: '0'
265
269
  requirements: []
266
- rubygems_version: 3.6.1
270
+ rubygems_version: 3.6.2
267
271
  specification_version: 4
268
272
  summary: Automatic Rails code style checking tool.
269
273
  test_files: []