rubocop-rails 2.27.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.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +16 -0
- data/config/default.yml +22 -0
- data/lib/rubocop/cop/mixin/index_method.rb +12 -5
- data/lib/rubocop/cop/mixin/routes_helper.rb +20 -0
- data/lib/rubocop/cop/rails/add_column_index.rb +1 -0
- data/lib/rubocop/cop/rails/bulk_change_table.rb +1 -0
- data/lib/rubocop/cop/rails/dangerous_column_names.rb +2 -0
- data/lib/rubocop/cop/rails/file_path.rb +3 -0
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +7 -0
- data/lib/rubocop/cop/rails/index_by.rb +28 -12
- data/lib/rubocop/cop/rails/index_with.rb +28 -12
- data/lib/rubocop/cop/rails/match_route.rb +1 -9
- data/lib/rubocop/cop/rails/multiple_route_paths.rb +50 -0
- data/lib/rubocop/cop/rails/not_null_column.rb +6 -2
- data/lib/rubocop/cop/rails/pluck.rb +20 -0
- data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +1 -1
- data/lib/rubocop/cop/rails/reversible_migration.rb +3 -1
- data/lib/rubocop/cop/rails/save_bang.rb +1 -1
- data/lib/rubocop/cop/rails/schema_comment.rb +1 -0
- data/lib/rubocop/cop/rails/select_map.rb +1 -1
- data/lib/rubocop/cop/rails/strong_parameters_expect.rb +104 -0
- data/lib/rubocop/cop/rails/three_state_boolean_column.rb +2 -1
- data/lib/rubocop/cop/rails/time_zone.rb +10 -6
- data/lib/rubocop/cop/rails_cops.rb +3 -0
- data/lib/rubocop/rails/migration_file_skippable.rb +54 -0
- data/lib/rubocop/rails/version.rb +1 -1
- data/lib/rubocop/rails.rb +0 -1
- data/lib/rubocop-rails.rb +3 -0
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 210e46df0db68aeef5761f3afeeb86d62a52015be55190d3e6e1fd65368721be
|
4
|
+
data.tar.gz: 7ff2ef268227cf39cb23b0479a8cf19c4ecc2acdf62c18f8e1766756e5965119
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c84c5690318920b054525a7fc21ca4e0f067a2093757f7ff8182d429587c94898ba1ea1160893fe29e9a8ef9427f6cc076f613f283b5b857da0529fea7654a47
|
7
|
+
data.tar.gz: ca501a5661bcebf2a6769a8cba620c637872e17023ac19fe8f6415b5ff4305d735ead0d24c7ffaee46f24c6cbeb466fcffeebdfb9cd96836211ab446643fc2a7
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -83,6 +83,22 @@ gems.locked file to find the version of Rails that has been bound to the
|
|
83
83
|
application. If neither of those files exist, RuboCop will use Rails 5.0
|
84
84
|
as the default.
|
85
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
|
+
|
86
102
|
## Rails configuration tip
|
87
103
|
|
88
104
|
In Rails 6.1+, add the following `config.generators.after_generate` setting to
|
data/config/default.yml
CHANGED
@@ -25,6 +25,10 @@ AllCops:
|
|
25
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`
|
@@ -694,6 +698,15 @@ Rails/MigrationClassName:
|
|
694
698
|
Include:
|
695
699
|
- db/**/*.rb
|
696
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
|
+
|
697
710
|
Rails/NegateInclude:
|
698
711
|
Description: 'Prefer `collection.exclude?(obj)` over `!collection.include?(obj)`.'
|
699
712
|
StyleGuide: 'https://rails.rubystyle.guide#exclude'
|
@@ -1068,6 +1081,15 @@ Rails/StripHeredoc:
|
|
1068
1081
|
Enabled: pending
|
1069
1082
|
VersionAdded: '2.15'
|
1070
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
|
+
|
1071
1093
|
Rails/TableNameAssignment:
|
1072
1094
|
Description: >-
|
1073
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)
|
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
|
-
|
126
|
-
|
127
|
-
|
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
|
-
|
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
|
@@ -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)
|
@@ -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
|
-
|
33
|
-
(
|
34
|
-
|
35
|
-
|
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
|
-
|
41
|
-
(
|
42
|
-
|
43
|
-
|
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
|
-
|
52
|
-
(
|
53
|
-
|
54
|
-
|
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
|
-
|
36
|
-
(
|
37
|
-
|
38
|
-
|
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
|
-
|
44
|
-
(
|
45
|
-
|
46
|
-
|
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
|
-
|
55
|
-
(
|
56
|
-
|
57
|
-
|
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
|
@@ -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,10 +41,14 @@ module RuboCop
|
|
41
41
|
# change_column_null :products, :category_id, false
|
42
42
|
class NotNullColumn < Base
|
43
43
|
include DatabaseTypeResolvable
|
44
|
+
include MigrationsHelper
|
44
45
|
|
45
46
|
MSG = 'Do not add a NOT NULL column without a default value.'
|
46
47
|
RESTRICT_ON_SEND = %i[add_column add_reference].freeze
|
47
48
|
|
49
|
+
VIRTUAL_TYPE_VALUES = [:virtual, 'virtual'].freeze
|
50
|
+
TEXT_TYPE_VALUES = [:text, 'text'].freeze
|
51
|
+
|
48
52
|
def_node_matcher :add_not_null_column?, <<~PATTERN
|
49
53
|
(send nil? :add_column _ _ $_ (hash $...))
|
50
54
|
PATTERN
|
@@ -91,8 +95,8 @@ module RuboCop
|
|
91
95
|
|
92
96
|
def check_column(type, pairs)
|
93
97
|
if type.respond_to?(:value)
|
94
|
-
return if
|
95
|
-
return if (type.value
|
98
|
+
return if VIRTUAL_TYPE_VALUES.include?(type.value)
|
99
|
+
return if TEXT_TYPE_VALUES.include?(type.value) && database == MYSQL
|
96
100
|
end
|
97
101
|
|
98
102
|
check_pairs(pairs)
|
@@ -9,6 +9,24 @@ module RuboCop
|
|
9
9
|
# element in an enumerable. When called on an Active Record relation, it
|
10
10
|
# results in a more efficient query that only selects the necessary key.
|
11
11
|
#
|
12
|
+
# NOTE: If the receiver's relation is not loaded and `pluck` is used inside an iteration,
|
13
|
+
# it may result in N+1 queries because `pluck` queries the database on each iteration.
|
14
|
+
# This cop ignores offenses for `map/collect` when they are suspected to be part of an iteration
|
15
|
+
# to prevent such potential issues.
|
16
|
+
#
|
17
|
+
# [source,ruby]
|
18
|
+
# ----
|
19
|
+
# users = User.all
|
20
|
+
# 5.times do
|
21
|
+
# users.map { |user| user[:foo] } # Only one query is executed
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# users = User.all
|
25
|
+
# 5.times do
|
26
|
+
# users.pluck(:id) # A query is executed on every iteration
|
27
|
+
# end
|
28
|
+
# ----
|
29
|
+
#
|
12
30
|
# @safety
|
13
31
|
# This cop is unsafe because model can use column aliases.
|
14
32
|
#
|
@@ -42,6 +60,8 @@ module RuboCop
|
|
42
60
|
PATTERN
|
43
61
|
|
44
62
|
def on_block(node)
|
63
|
+
return if node.each_ancestor(:block, :numblock).any?
|
64
|
+
|
45
65
|
pluck_candidate?(node) do |argument, key|
|
46
66
|
next if key.regexp_type? || !use_one_block_argument?(argument)
|
47
67
|
|
@@ -174,7 +174,7 @@ module RuboCop
|
|
174
174
|
parent = node.parent
|
175
175
|
return false unless POSSIBLE_ENUMERABLE_BLOCK_METHODS.include?(parent.method_name)
|
176
176
|
|
177
|
-
parent.
|
177
|
+
parent.block_literal? || parent.first_argument&.block_pass_type?
|
178
178
|
end
|
179
179
|
|
180
180
|
def sensitive_association_method?(node)
|
@@ -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? ||
|
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
|
@@ -54,7 +54,7 @@ module RuboCop
|
|
54
54
|
|
55
55
|
# rubocop:disable Metrics/AbcSize
|
56
56
|
def autocorrect(corrector, select_node, node, preferred_method)
|
57
|
-
corrector.remove(select_node.
|
57
|
+
corrector.remove(select_node.parent.loc.dot)
|
58
58
|
corrector.remove(select_node.loc.selector.begin.join(select_node.source_range.end))
|
59
59
|
corrector.replace(node.loc.selector.begin.join(node.source_range.end), preferred_method)
|
60
60
|
end
|
@@ -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,8 +18,9 @@ module RuboCop
|
|
18
18
|
# t.boolean :active, default: true, null: false
|
19
19
|
#
|
20
20
|
class ThreeStateBooleanColumn < Base
|
21
|
-
|
21
|
+
include MigrationsHelper
|
22
22
|
|
23
|
+
MSG = 'Boolean columns should always have a default value and a `NOT NULL` constraint.'
|
23
24
|
RESTRICT_ON_SEND = %i[add_column column boolean].freeze
|
24
25
|
|
25
26
|
def_node_matcher :three_state_boolean?, <<~PATTERN
|
@@ -97,11 +97,9 @@ module RuboCop
|
|
97
97
|
end
|
98
98
|
|
99
99
|
def autocorrect_time_new(node, corrector)
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
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
|
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.
|
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:
|
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.
|
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.
|
270
|
+
rubygems_version: 3.6.2
|
267
271
|
specification_version: 4
|
268
272
|
summary: Automatic Rails code style checking tool.
|
269
273
|
test_files: []
|