rubocop-rails 2.26.2 → 2.29.1
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 +36 -0
- data/config/default.yml +27 -5
- data/lib/rubocop/cop/mixin/index_method.rb +12 -5
- data/lib/rubocop/cop/mixin/routes_helper.rb +20 -0
- data/lib/rubocop/cop/mixin/target_rails_version.rb +3 -5
- 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/duplicate_association.rb +8 -4
- data/lib/rubocop/cop/rails/enum_syntax.rb +2 -0
- data/lib/rubocop/cop/rails/env_local.rb +26 -3
- data/lib/rubocop/cop/rails/file_path.rb +25 -2
- 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/root_pathname_methods.rb +6 -1
- data/lib/rubocop/cop/rails/save_bang.rb +4 -3
- data/lib/rubocop/cop/rails/schema_comment.rb +1 -0
- data/lib/rubocop/cop/rails/select_map.rb +3 -2
- data/lib/rubocop/cop/rails/squished_sql_heredocs.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 +13 -6
- data/lib/rubocop/cop/rails/transaction_exit_statement.rb +5 -0
- data/lib/rubocop/cop/rails/where_range.rb +1 -1
- 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 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f6fa236fdf79709d4d0baec3c3ed01bc254e807dbd5ec39229374187b1d3d30c
|
|
4
|
+
data.tar.gz: 99b29d27dd97c1adc12efb3d60a55370f6c4ad363bd6de51ca11ac33f3b1cd8a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 422221a038428edaf91477ef0ede6939023a686f3a62c14242efbfa1cb31be9d8f504eaf15ac103875696b5584b690cabcc86a4041b5c42cfd4480aa4ad27dbd
|
|
7
|
+
data.tar.gz: 8240dcae47150b3137a980e731d69133df5e54364b62014d296b51715b1e3f47077b828d4f256191c79d8a14121e0d5cce9b06857f54b632e398216290e31f29
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -63,6 +63,42 @@ 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
104
|
In Rails 6.1+, add the following `config.generators.after_generate` setting to
|
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?
|
|
21
|
-
# for TargetRailsVersion then it is used.
|
|
22
|
-
# as a float (
|
|
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.
|
|
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
|
|
@@ -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
|
|
@@ -63,11 +63,15 @@ module RuboCop
|
|
|
63
63
|
private
|
|
64
64
|
|
|
65
65
|
def register_offense(name, nodes, message_template)
|
|
66
|
-
nodes.
|
|
67
|
-
add_offense(node, message: format(message_template, name: name)) do |corrector|
|
|
68
|
-
next if same_line?(nodes.last, node)
|
|
66
|
+
last_node = nodes.last
|
|
69
67
|
|
|
70
|
-
|
|
68
|
+
nodes.each_with_index do |node, index|
|
|
69
|
+
add_offense(node, message: format(message_template, name: name)) do |corrector|
|
|
70
|
+
if index.zero?
|
|
71
|
+
corrector.replace(node, last_node.source)
|
|
72
|
+
else
|
|
73
|
+
corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
|
|
74
|
+
end
|
|
71
75
|
end
|
|
72
76
|
end
|
|
73
77
|
end
|
|
@@ -19,20 +19,33 @@ module RuboCop
|
|
|
19
19
|
extend TargetRailsVersion
|
|
20
20
|
|
|
21
21
|
MSG = 'Use `Rails.env.local?` instead.'
|
|
22
|
+
MSG_NEGATED = 'Use `!Rails.env.local?` instead.'
|
|
22
23
|
LOCAL_ENVIRONMENTS = %i[development? test?].to_set.freeze
|
|
23
24
|
|
|
24
25
|
minimum_target_rails_version 7.1
|
|
25
26
|
|
|
26
|
-
# @!method
|
|
27
|
-
def_node_matcher :
|
|
27
|
+
# @!method rails_env_local_or?(node)
|
|
28
|
+
def_node_matcher :rails_env_local_or?, <<~PATTERN
|
|
28
29
|
(or
|
|
29
30
|
(send (send (const {cbase nil? } :Rails) :env) $%LOCAL_ENVIRONMENTS)
|
|
30
31
|
(send (send (const {cbase nil? } :Rails) :env) $%LOCAL_ENVIRONMENTS)
|
|
31
32
|
)
|
|
32
33
|
PATTERN
|
|
33
34
|
|
|
35
|
+
# @!method rails_env_local_and?(node)
|
|
36
|
+
def_node_matcher :rails_env_local_and?, <<~PATTERN
|
|
37
|
+
(and
|
|
38
|
+
(send
|
|
39
|
+
(send (send (const {cbase nil? } :Rails) :env) $%LOCAL_ENVIRONMENTS)
|
|
40
|
+
:!)
|
|
41
|
+
(send
|
|
42
|
+
(send (send (const {cbase nil? } :Rails) :env) $%LOCAL_ENVIRONMENTS)
|
|
43
|
+
:!)
|
|
44
|
+
)
|
|
45
|
+
PATTERN
|
|
46
|
+
|
|
34
47
|
def on_or(node)
|
|
35
|
-
|
|
48
|
+
rails_env_local_or?(node) do |*environments|
|
|
36
49
|
next unless environments.to_set == LOCAL_ENVIRONMENTS
|
|
37
50
|
|
|
38
51
|
add_offense(node) do |corrector|
|
|
@@ -40,6 +53,16 @@ module RuboCop
|
|
|
40
53
|
end
|
|
41
54
|
end
|
|
42
55
|
end
|
|
56
|
+
|
|
57
|
+
def on_and(node)
|
|
58
|
+
rails_env_local_and?(node) do |*environments|
|
|
59
|
+
next unless environments.to_set == LOCAL_ENVIRONMENTS
|
|
60
|
+
|
|
61
|
+
add_offense(node, message: MSG_NEGATED) do |corrector|
|
|
62
|
+
corrector.replace(node, '!Rails.env.local?')
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
43
66
|
end
|
|
44
67
|
end
|
|
45
68
|
end
|
|
@@ -66,6 +66,8 @@ module RuboCop
|
|
|
66
66
|
|
|
67
67
|
def on_send(node)
|
|
68
68
|
check_for_file_join_with_rails_root(node)
|
|
69
|
+
return unless node.receiver
|
|
70
|
+
|
|
69
71
|
check_for_rails_root_join_with_slash_separated_path(node)
|
|
70
72
|
check_for_rails_root_join_with_string_arguments(node)
|
|
71
73
|
end
|
|
@@ -76,6 +78,7 @@ module RuboCop
|
|
|
76
78
|
rails_root_index = find_rails_root_index(node)
|
|
77
79
|
slash_node = node.children[rails_root_index + 1]
|
|
78
80
|
return unless slash_node&.str_type? && slash_node.source.start_with?(File::SEPARATOR)
|
|
81
|
+
return unless node.children[rails_root_index].children.first.send_type?
|
|
79
82
|
|
|
80
83
|
register_offense(node, require_to_s: false) do |corrector|
|
|
81
84
|
autocorrect_slash_after_rails_root_in_dstr(corrector, node, rails_root_index)
|
|
@@ -170,7 +173,17 @@ module RuboCop
|
|
|
170
173
|
end
|
|
171
174
|
|
|
172
175
|
def autocorrect_file_join(corrector, node)
|
|
176
|
+
replace_receiver_with_rails_root(corrector, node)
|
|
177
|
+
remove_first_argument_with_comma(corrector, node)
|
|
178
|
+
process_arguments(corrector, node.arguments)
|
|
179
|
+
append_to_string_conversion(corrector, node)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def replace_receiver_with_rails_root(corrector, node)
|
|
173
183
|
corrector.replace(node.receiver, 'Rails.root')
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def remove_first_argument_with_comma(corrector, node)
|
|
174
187
|
corrector.remove(
|
|
175
188
|
range_with_surrounding_space(
|
|
176
189
|
range_with_surrounding_comma(
|
|
@@ -180,9 +193,19 @@ module RuboCop
|
|
|
180
193
|
side: :right
|
|
181
194
|
)
|
|
182
195
|
)
|
|
183
|
-
|
|
184
|
-
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def process_arguments(corrector, arguments)
|
|
199
|
+
arguments.each do |argument|
|
|
200
|
+
if argument.str_type?
|
|
201
|
+
corrector.replace(argument, argument.value.delete_prefix('/').inspect)
|
|
202
|
+
elsif argument.array_type?
|
|
203
|
+
corrector.replace(argument, "*#{argument.source}")
|
|
204
|
+
end
|
|
185
205
|
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def append_to_string_conversion(corrector, node)
|
|
186
209
|
corrector.insert_after(node, '.to_s')
|
|
187
210
|
end
|
|
188
211
|
|
|
@@ -40,6 +40,10 @@ module RuboCop
|
|
|
40
40
|
(hash (kwsplat _))
|
|
41
41
|
PATTERN
|
|
42
42
|
|
|
43
|
+
def_node_matcher :forwarded_kwrestarg?, <<~PATTERN
|
|
44
|
+
(hash (forwarded-kwrestarg))
|
|
45
|
+
PATTERN
|
|
46
|
+
|
|
43
47
|
def_node_matcher :include_rack_test_methods?, <<~PATTERN
|
|
44
48
|
(send nil? :include
|
|
45
49
|
(const
|
|
@@ -83,7 +87,9 @@ module RuboCop
|
|
|
83
87
|
end
|
|
84
88
|
end
|
|
85
89
|
|
|
90
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
86
91
|
def needs_conversion?(data)
|
|
92
|
+
return false if data.forwarded_args_type? || forwarded_kwrestarg?(data)
|
|
87
93
|
return true unless data.hash_type?
|
|
88
94
|
return false if kwsplat_hash?(data)
|
|
89
95
|
|
|
@@ -91,6 +97,7 @@ module RuboCop
|
|
|
91
97
|
special_keyword_arg?(pair.key) || (format_arg?(pair.key) && data.pairs.one?)
|
|
92
98
|
end
|
|
93
99
|
end
|
|
100
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
94
101
|
|
|
95
102
|
def special_keyword_arg?(node)
|
|
96
103
|
node.sym_type? && KEYWORD_ARGS.include?(node.value)
|
|
@@ -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
|
|
@@ -237,7 +237,12 @@ module RuboCop
|
|
|
237
237
|
end
|
|
238
238
|
|
|
239
239
|
replacement = "#{path_replacement}.#{method}"
|
|
240
|
-
|
|
240
|
+
|
|
241
|
+
if args.any?
|
|
242
|
+
formatted_args = args.map { |arg| arg.array_type? ? "*#{arg.source}" : arg.source }
|
|
243
|
+
replacement += "(#{formatted_args.join(', ')})"
|
|
244
|
+
end
|
|
245
|
+
|
|
241
246
|
replacement
|
|
242
247
|
end
|
|
243
248
|
|
|
@@ -244,7 +244,7 @@ module RuboCop
|
|
|
244
244
|
end
|
|
245
245
|
|
|
246
246
|
def operator_or_single_negative?(node)
|
|
247
|
-
node.
|
|
247
|
+
node.operator_keyword? || single_negative?(node)
|
|
248
248
|
end
|
|
249
249
|
|
|
250
250
|
def conditional?(parent)
|
|
@@ -328,8 +328,9 @@ module RuboCop
|
|
|
328
328
|
end
|
|
329
329
|
|
|
330
330
|
def return_value_assigned?(node)
|
|
331
|
-
assignment = assignable_node(node).parent
|
|
332
|
-
|
|
331
|
+
return false unless (assignment = assignable_node(node).parent)
|
|
332
|
+
|
|
333
|
+
assignment.assignment?
|
|
333
334
|
end
|
|
334
335
|
|
|
335
336
|
def persist_method?(node, methods = RESTRICT_ON_SEND)
|
|
@@ -40,12 +40,13 @@ module RuboCop
|
|
|
40
40
|
autocorrect(corrector, select_node, node, preferred_method)
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
|
+
alias on_csend on_send
|
|
43
44
|
|
|
44
45
|
private
|
|
45
46
|
|
|
46
47
|
def find_select_node(node, column_name)
|
|
47
48
|
node.descendants.detect do |select_candidate|
|
|
48
|
-
next if !select_candidate.
|
|
49
|
+
next if !select_candidate.call_type? || !select_candidate.method?(:select)
|
|
49
50
|
|
|
50
51
|
match_column_name?(select_candidate, column_name)
|
|
51
52
|
end
|
|
@@ -53,7 +54,7 @@ module RuboCop
|
|
|
53
54
|
|
|
54
55
|
# rubocop:disable Metrics/AbcSize
|
|
55
56
|
def autocorrect(corrector, select_node, node, preferred_method)
|
|
56
|
-
corrector.remove(select_node.
|
|
57
|
+
corrector.remove(select_node.parent.loc.dot)
|
|
57
58
|
corrector.remove(select_node.loc.selector.begin.join(select_node.source_range.end))
|
|
58
59
|
corrector.replace(node.loc.selector.begin.join(node.source_range.end), preferred_method)
|
|
59
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.receiver.source_range.end.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
|
|
@@ -28,6 +28,8 @@ module RuboCop
|
|
|
28
28
|
# Time.zone.now
|
|
29
29
|
# Time.zone.parse('2015-03-02T19:05:37')
|
|
30
30
|
# Time.zone.parse('2015-03-02T19:05:37Z') # Respect ISO 8601 format with timezone specifier.
|
|
31
|
+
# Time.parse('2015-03-02T19:05:37Z') # Also respects ISO 8601
|
|
32
|
+
# '2015-03-02T19:05:37Z'.to_time # Also respects ISO 8601
|
|
31
33
|
#
|
|
32
34
|
# @example EnforcedStyle: flexible (default)
|
|
33
35
|
# # `flexible` allows usage of `in_time_zone` instead of `zone`.
|
|
@@ -67,6 +69,7 @@ module RuboCop
|
|
|
67
69
|
|
|
68
70
|
def on_send(node)
|
|
69
71
|
return if !node.receiver&.str_type? || !node.method?(:to_time)
|
|
72
|
+
return if attach_timezone_specifier?(node.receiver)
|
|
70
73
|
|
|
71
74
|
add_offense(node.loc.selector, message: MSG_STRING_TO_TIME) do |corrector|
|
|
72
75
|
corrector.replace(node, "Time.zone.parse(#{node.receiver.source})") unless node.csend_type?
|
|
@@ -94,11 +97,9 @@ module RuboCop
|
|
|
94
97
|
end
|
|
95
98
|
|
|
96
99
|
def autocorrect_time_new(node, corrector)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
corrector.replace(node.loc.selector, 'now')
|
|
101
|
-
end
|
|
100
|
+
replacement = replacement(node)
|
|
101
|
+
|
|
102
|
+
corrector.replace(node.loc.selector, replacement)
|
|
102
103
|
end
|
|
103
104
|
|
|
104
105
|
# remove redundant `.in_time_zone` from `Time.zone.now.in_time_zone`
|
|
@@ -180,7 +181,7 @@ module RuboCop
|
|
|
180
181
|
|
|
181
182
|
def safe_method(method_name, node)
|
|
182
183
|
if %w[new current].include?(method_name)
|
|
183
|
-
node
|
|
184
|
+
replacement(node)
|
|
184
185
|
else
|
|
185
186
|
method_name
|
|
186
187
|
end
|
|
@@ -256,6 +257,12 @@ module RuboCop
|
|
|
256
257
|
pair.key.sym_type? && pair.key.value == :in && !pair.value.nil_type?
|
|
257
258
|
end
|
|
258
259
|
end
|
|
260
|
+
|
|
261
|
+
def replacement(node)
|
|
262
|
+
return 'now' unless node.arguments?
|
|
263
|
+
|
|
264
|
+
node.first_argument.str_type? ? 'parse' : 'local'
|
|
265
|
+
end
|
|
259
266
|
end
|
|
260
267
|
end
|
|
261
268
|
end
|
|
@@ -15,6 +15,10 @@ module RuboCop
|
|
|
15
15
|
#
|
|
16
16
|
# If you are defining custom transaction methods, you can configure it with `TransactionMethods`.
|
|
17
17
|
#
|
|
18
|
+
# NOTE: This cop is disabled on Rails >= 7.2 because transactions were restored
|
|
19
|
+
# to their historical behavior. In Rails 7.1, the behavior is controlled with
|
|
20
|
+
# the config `active_record.commit_transaction_on_non_local_return`.
|
|
21
|
+
#
|
|
18
22
|
# @example
|
|
19
23
|
# # bad
|
|
20
24
|
# ApplicationRecord.transaction do
|
|
@@ -76,6 +80,7 @@ module RuboCop
|
|
|
76
80
|
PATTERN
|
|
77
81
|
|
|
78
82
|
def on_send(node)
|
|
83
|
+
return if target_rails_version >= 7.2
|
|
79
84
|
return unless in_transaction_block?(node)
|
|
80
85
|
|
|
81
86
|
exit_statements(node.parent.body).each do |statement_node|
|
|
@@ -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,16 +1,15 @@
|
|
|
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.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bozhidar Batsov
|
|
8
8
|
- Jonas Arvidsson
|
|
9
9
|
- Yuji Nakayama
|
|
10
|
-
autorequire:
|
|
11
10
|
bindir: bin
|
|
12
11
|
cert_chain: []
|
|
13
|
-
date:
|
|
12
|
+
date: 2025-01-25 00:00:00.000000000 Z
|
|
14
13
|
dependencies:
|
|
15
14
|
- !ruby/object:Gem::Dependency
|
|
16
15
|
name: activesupport
|
|
@@ -102,6 +101,7 @@ files:
|
|
|
102
101
|
- lib/rubocop/cop/mixin/enforce_superclass.rb
|
|
103
102
|
- lib/rubocop/cop/mixin/index_method.rb
|
|
104
103
|
- lib/rubocop/cop/mixin/migrations_helper.rb
|
|
104
|
+
- lib/rubocop/cop/mixin/routes_helper.rb
|
|
105
105
|
- lib/rubocop/cop/mixin/target_rails_version.rb
|
|
106
106
|
- lib/rubocop/cop/rails/action_controller_flash_before_render.rb
|
|
107
107
|
- lib/rubocop/cop/rails/action_controller_test_case.rb
|
|
@@ -171,6 +171,7 @@ files:
|
|
|
171
171
|
- lib/rubocop/cop/rails/mailer_name.rb
|
|
172
172
|
- lib/rubocop/cop/rails/match_route.rb
|
|
173
173
|
- lib/rubocop/cop/rails/migration_class_name.rb
|
|
174
|
+
- lib/rubocop/cop/rails/multiple_route_paths.rb
|
|
174
175
|
- lib/rubocop/cop/rails/negate_include.rb
|
|
175
176
|
- lib/rubocop/cop/rails/not_null_column.rb
|
|
176
177
|
- lib/rubocop/cop/rails/order_by_id.rb
|
|
@@ -214,6 +215,7 @@ files:
|
|
|
214
215
|
- lib/rubocop/cop/rails/skips_model_validations.rb
|
|
215
216
|
- lib/rubocop/cop/rails/squished_sql_heredocs.rb
|
|
216
217
|
- lib/rubocop/cop/rails/strip_heredoc.rb
|
|
218
|
+
- lib/rubocop/cop/rails/strong_parameters_expect.rb
|
|
217
219
|
- lib/rubocop/cop/rails/table_name_assignment.rb
|
|
218
220
|
- lib/rubocop/cop/rails/three_state_boolean_column.rb
|
|
219
221
|
- lib/rubocop/cop/rails/time_zone.rb
|
|
@@ -237,6 +239,7 @@ files:
|
|
|
237
239
|
- lib/rubocop/cop/rails_cops.rb
|
|
238
240
|
- lib/rubocop/rails.rb
|
|
239
241
|
- lib/rubocop/rails/inject.rb
|
|
242
|
+
- lib/rubocop/rails/migration_file_skippable.rb
|
|
240
243
|
- lib/rubocop/rails/schema_loader.rb
|
|
241
244
|
- lib/rubocop/rails/schema_loader/schema.rb
|
|
242
245
|
- lib/rubocop/rails/version.rb
|
|
@@ -247,10 +250,9 @@ metadata:
|
|
|
247
250
|
homepage_uri: https://docs.rubocop.org/rubocop-rails/
|
|
248
251
|
changelog_uri: https://github.com/rubocop/rubocop-rails/blob/master/CHANGELOG.md
|
|
249
252
|
source_code_uri: https://github.com/rubocop/rubocop-rails/
|
|
250
|
-
documentation_uri: https://docs.rubocop.org/rubocop-rails/2.
|
|
253
|
+
documentation_uri: https://docs.rubocop.org/rubocop-rails/2.29/
|
|
251
254
|
bug_tracker_uri: https://github.com/rubocop/rubocop-rails/issues
|
|
252
255
|
rubygems_mfa_required: 'true'
|
|
253
|
-
post_install_message:
|
|
254
256
|
rdoc_options: []
|
|
255
257
|
require_paths:
|
|
256
258
|
- lib
|
|
@@ -265,8 +267,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
265
267
|
- !ruby/object:Gem::Version
|
|
266
268
|
version: '0'
|
|
267
269
|
requirements: []
|
|
268
|
-
rubygems_version: 3.
|
|
269
|
-
signing_key:
|
|
270
|
+
rubygems_version: 3.6.2
|
|
270
271
|
specification_version: 4
|
|
271
272
|
summary: Automatic Rails code style checking tool.
|
|
272
273
|
test_files: []
|