rubocop-rails 2.27.0 → 2.30.2
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 +23 -6
- data/config/default.yml +22 -0
- data/lib/rubocop/cop/mixin/active_record_helper.rb +1 -1
- data/lib/rubocop/cop/mixin/index_method.rb +68 -61
- 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/belongs_to.rb +1 -1
- data/lib/rubocop/cop/rails/blank.rb +1 -1
- data/lib/rubocop/cop/rails/bulk_change_table.rb +1 -0
- data/lib/rubocop/cop/rails/content_tag.rb +1 -1
- data/lib/rubocop/cop/rails/dangerous_column_names.rb +2 -0
- data/lib/rubocop/cop/rails/delegate.rb +50 -7
- data/lib/rubocop/cop/rails/duplicate_association.rb +8 -4
- data/lib/rubocop/cop/rails/file_path.rb +61 -9
- 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/inquiry.rb +1 -1
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +11 -1
- 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 +21 -1
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +1 -1
- data/lib/rubocop/cop/rails/presence.rb +1 -1
- data/lib/rubocop/cop/rails/present.rb +1 -1
- 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 +7 -6
- 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/skips_model_validations.rb +1 -1
- data/lib/rubocop/cop/rails/strip_heredoc.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/uniq_before_pluck.rb +10 -33
- data/lib/rubocop/cop/rails/unique_validation_without_index.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/plugin.rb +48 -0
- data/lib/rubocop/rails/version.rb +1 -1
- data/lib/rubocop/rails.rb +1 -8
- data/lib/rubocop-rails.rb +4 -4
- metadata +28 -9
- data/lib/rubocop/rails/inject.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69854cebe11dde8160c57b94c6ebbe60e7df48c1357aae64b932f73bb2f230ad
|
4
|
+
data.tar.gz: 04b388703ccde52554cb01f0e04a50291beada4dec9199fbb46b3dd750d8a4ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 79fda1fbb14ab98b4472576b7cb776294f3c46eb56c3b0751e47c5fba308615b4f1579a41ea3b14a8c2fb22e19e693ea1e615fe48c5d9fff6f8c857ef887a53f
|
7
|
+
data.tar.gz: f219f779b57f3ecd44937c02e85872058b453bdd7ce5c83cc1d6ac05175b2c93c01d2548a02c9b46ca26bc2dca6af116611a9d9595d81b53e5a165e47a8b6cf4
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -31,13 +31,13 @@ ways to do this:
|
|
31
31
|
Put this into your `.rubocop.yml`.
|
32
32
|
|
33
33
|
```yaml
|
34
|
-
|
34
|
+
plugins: rubocop-rails
|
35
35
|
```
|
36
36
|
|
37
37
|
Alternatively, use the following array notation when specifying multiple extensions.
|
38
38
|
|
39
39
|
```yaml
|
40
|
-
|
40
|
+
plugins:
|
41
41
|
- rubocop-other-extension
|
42
42
|
- rubocop-rails
|
43
43
|
```
|
@@ -45,21 +45,22 @@ require:
|
|
45
45
|
Now you can run `rubocop` and it will automatically load the RuboCop Rails
|
46
46
|
cops together with the standard cops.
|
47
47
|
|
48
|
+
> [!NOTE]
|
49
|
+
> The plugin system is supported in RuboCop 1.72+. In earlier versions, use `require` instead of `plugins`.
|
50
|
+
|
48
51
|
### Command line
|
49
52
|
|
50
53
|
```sh
|
51
|
-
$ rubocop --
|
54
|
+
$ rubocop --plugin rubocop-rails
|
52
55
|
```
|
53
56
|
|
54
|
-
Note: `--rails` option is required while `rubocop` command supports `--rails` option.
|
55
|
-
|
56
57
|
### Rake task
|
57
58
|
|
58
59
|
```ruby
|
59
60
|
require 'rubocop/rake_task'
|
60
61
|
|
61
62
|
RuboCop::RakeTask.new do |task|
|
62
|
-
task.
|
63
|
+
task.plugins << 'rubocop-rails'
|
63
64
|
end
|
64
65
|
```
|
65
66
|
|
@@ -83,6 +84,22 @@ gems.locked file to find the version of Rails that has been bound to the
|
|
83
84
|
application. If neither of those files exist, RuboCop will use Rails 5.0
|
84
85
|
as the default.
|
85
86
|
|
87
|
+
### `AllCops: MigratedSchemaVersion`
|
88
|
+
|
89
|
+
By specifying the `MigratedSchemaVersion` option, migration files that have already been run can be ignored.
|
90
|
+
When `MigratedSchemaVersion: '20241225000000'` is set, migration files lower than or equal to '20241225000000' will be ignored.
|
91
|
+
For example, to ignore db/migrate/20241225000000_create_articles.rb and earlier migrations you would configure it the following way:
|
92
|
+
|
93
|
+
```yaml
|
94
|
+
AllCops:
|
95
|
+
MigratedSchemaVersion: '20241225000000'
|
96
|
+
```
|
97
|
+
|
98
|
+
This prevents inspecting schema settings for already applied migration files.
|
99
|
+
Changing already applied migrations should be avoided because it can lead to the schema getting out of sync
|
100
|
+
between your local copy and what it actually is in production, depending on when `bin/rails db:migrate` was executed.
|
101
|
+
If you want to modify your schema to comply with the cops, you should instead create new migrations.
|
102
|
+
|
86
103
|
## Rails configuration tip
|
87
104
|
|
88
105
|
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: '19700101000000' # NOTE: Used as a sentinel value for the UNIX epoch time.
|
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.
|
@@ -87,7 +87,7 @@ module RuboCop
|
|
87
87
|
|
88
88
|
options.each_pair.find do |pair|
|
89
89
|
next unless pair.key.sym_type? && pair.key.value == :foreign_key
|
90
|
-
next unless pair.value.
|
90
|
+
next unless pair.value.type?(:sym, :str)
|
91
91
|
|
92
92
|
break pair.value.value.to_s
|
93
93
|
end
|
@@ -6,7 +6,72 @@ 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
|
-
|
9
|
+
# Internal helper class to hold match data
|
10
|
+
Captures = ::Struct.new(
|
11
|
+
:transformed_argname,
|
12
|
+
:transforming_body_expr
|
13
|
+
) do
|
14
|
+
def noop_transformation?
|
15
|
+
transforming_body_expr.lvar_type? && transforming_body_expr.children == [transformed_argname]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Internal helper class to hold autocorrect data
|
20
|
+
Autocorrection = ::Struct.new(:match, :block_node, :leading, :trailing) do
|
21
|
+
def self.from_each_with_object(node, match)
|
22
|
+
new(match, node, 0, 0)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.from_to_h(node, match)
|
26
|
+
new(match, node, 0, 0)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.from_map_to_h(node, match)
|
30
|
+
if node.block_literal?
|
31
|
+
strip_trailing_chars = 0
|
32
|
+
else
|
33
|
+
map_range = node.children.first.source_range
|
34
|
+
node_range = node.source_range
|
35
|
+
strip_trailing_chars = node_range.end_pos - map_range.end_pos
|
36
|
+
end
|
37
|
+
|
38
|
+
new(match, node.children.first, 0, strip_trailing_chars)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.from_hash_brackets_map(node, match)
|
42
|
+
new(match, node.children.last, "#{node.receiver.source}[".length, ']'.length)
|
43
|
+
end
|
44
|
+
|
45
|
+
def strip_prefix_and_suffix(node, corrector)
|
46
|
+
expression = node.source_range
|
47
|
+
corrector.remove_leading(expression, leading)
|
48
|
+
corrector.remove_trailing(expression, trailing)
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_new_method_name(new_method_name, corrector)
|
52
|
+
range = block_node.send_node.loc.selector
|
53
|
+
if (send_end = block_node.send_node.loc.end)
|
54
|
+
# If there are arguments (only true in the `each_with_object` case)
|
55
|
+
range = range.begin.join(send_end)
|
56
|
+
end
|
57
|
+
corrector.replace(range, new_method_name)
|
58
|
+
end
|
59
|
+
|
60
|
+
def set_new_arg_name(transformed_argname, corrector)
|
61
|
+
return if block_node.numblock_type?
|
62
|
+
|
63
|
+
corrector.replace(block_node.arguments, "|#{transformed_argname}|")
|
64
|
+
end
|
65
|
+
|
66
|
+
def set_new_body_expression(transforming_body_expr, corrector)
|
67
|
+
body_source = transforming_body_expr.source
|
68
|
+
body_source = "{ #{body_source} }" if transforming_body_expr.hash_type? && !transforming_body_expr.braces?
|
69
|
+
|
70
|
+
corrector.replace(block_node.body, body_source)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def on_block(node)
|
10
75
|
on_bad_each_with_object(node) do |*match|
|
11
76
|
handle_possible_offense(node, match, 'each_with_object')
|
12
77
|
end
|
@@ -18,6 +83,8 @@ module RuboCop
|
|
18
83
|
end
|
19
84
|
end
|
20
85
|
|
86
|
+
alias on_numblock on_block
|
87
|
+
|
21
88
|
def on_send(node)
|
22
89
|
on_bad_map_to_h(node) do |*match|
|
23
90
|
handle_possible_offense(node, match, 'map { ... }.to_h')
|
@@ -100,66 +167,6 @@ module RuboCop
|
|
100
167
|
correction.set_new_arg_name(captures.transformed_argname, corrector)
|
101
168
|
correction.set_new_body_expression(captures.transforming_body_expr, corrector)
|
102
169
|
end
|
103
|
-
|
104
|
-
# Internal helper class to hold match data
|
105
|
-
Captures = ::Struct.new(
|
106
|
-
:transformed_argname,
|
107
|
-
:transforming_body_expr
|
108
|
-
) do
|
109
|
-
def noop_transformation?
|
110
|
-
transforming_body_expr.lvar_type? && transforming_body_expr.children == [transformed_argname]
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
# Internal helper class to hold autocorrect data
|
115
|
-
Autocorrection = ::Struct.new(:match, :block_node, :leading, :trailing) do
|
116
|
-
def self.from_each_with_object(node, match)
|
117
|
-
new(match, node, 0, 0)
|
118
|
-
end
|
119
|
-
|
120
|
-
def self.from_to_h(node, match)
|
121
|
-
new(match, node, 0, 0)
|
122
|
-
end
|
123
|
-
|
124
|
-
def self.from_map_to_h(node, match)
|
125
|
-
strip_trailing_chars = 0
|
126
|
-
|
127
|
-
unless node.parent&.block_type?
|
128
|
-
map_range = node.children.first.source_range
|
129
|
-
node_range = node.source_range
|
130
|
-
strip_trailing_chars = node_range.end_pos - map_range.end_pos
|
131
|
-
end
|
132
|
-
|
133
|
-
new(match, node.children.first, 0, strip_trailing_chars)
|
134
|
-
end
|
135
|
-
|
136
|
-
def self.from_hash_brackets_map(node, match)
|
137
|
-
new(match, node.children.last, "#{node.receiver.source}[".length, ']'.length)
|
138
|
-
end
|
139
|
-
|
140
|
-
def strip_prefix_and_suffix(node, corrector)
|
141
|
-
expression = node.source_range
|
142
|
-
corrector.remove_leading(expression, leading)
|
143
|
-
corrector.remove_trailing(expression, trailing)
|
144
|
-
end
|
145
|
-
|
146
|
-
def set_new_method_name(new_method_name, corrector)
|
147
|
-
range = block_node.send_node.loc.selector
|
148
|
-
if (send_end = block_node.send_node.loc.end)
|
149
|
-
# If there are arguments (only true in the `each_with_object` case)
|
150
|
-
range = range.begin.join(send_end)
|
151
|
-
end
|
152
|
-
corrector.replace(range, new_method_name)
|
153
|
-
end
|
154
|
-
|
155
|
-
def set_new_arg_name(transformed_argname, corrector)
|
156
|
-
corrector.replace(block_node.arguments, "|#{transformed_argname}|")
|
157
|
-
end
|
158
|
-
|
159
|
-
def set_new_body_expression(transforming_body_expr, corrector)
|
160
|
-
corrector.replace(block_node.body, transforming_body_expr.source)
|
161
|
-
end
|
162
|
-
end
|
163
170
|
end
|
164
171
|
end
|
165
172
|
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
|
@@ -123,7 +123,7 @@ module RuboCop
|
|
123
123
|
def on_if(node)
|
124
124
|
return unless cop_config['UnlessPresent']
|
125
125
|
return unless node.unless?
|
126
|
-
return if node.else? && config.
|
126
|
+
return if node.else? && config.cop_enabled?('Style/UnlessElse')
|
127
127
|
|
128
128
|
unless_present?(node) do |method_call, receiver|
|
129
129
|
range = unless_condition(node, method_call)
|
@@ -68,12 +68,13 @@ module RuboCop
|
|
68
68
|
|
69
69
|
def_node_matcher :delegate?, <<~PATTERN
|
70
70
|
(def _method_name _args
|
71
|
-
(send {(send nil? _) (self)} _ ...))
|
71
|
+
(send {(send nil? _) (self) (send (self) :class) ({cvar gvar ivar} _) (const _ _)} _ ...))
|
72
72
|
PATTERN
|
73
73
|
|
74
74
|
def on_def(node)
|
75
75
|
return unless trivial_delegate?(node)
|
76
76
|
return if private_or_protected_delegation(node)
|
77
|
+
return if module_function_declared?(node)
|
77
78
|
|
78
79
|
register_offense(node)
|
79
80
|
end
|
@@ -82,15 +83,40 @@ module RuboCop
|
|
82
83
|
|
83
84
|
def register_offense(node)
|
84
85
|
add_offense(node.loc.keyword) do |corrector|
|
85
|
-
|
86
|
+
receiver = determine_register_offense_receiver(node.body.receiver)
|
87
|
+
delegation = build_delegation(node, receiver)
|
86
88
|
|
87
|
-
|
89
|
+
corrector.replace(node, delegation)
|
90
|
+
end
|
91
|
+
end
|
88
92
|
|
89
|
-
|
90
|
-
|
93
|
+
def determine_register_offense_receiver(receiver)
|
94
|
+
case receiver.type
|
95
|
+
when :self
|
96
|
+
'self'
|
97
|
+
when :const
|
98
|
+
full_name = full_const_name(receiver)
|
99
|
+
full_name.include?('::') ? ":'#{full_name}'" : ":#{full_name}"
|
100
|
+
when :cvar, :gvar, :ivar
|
101
|
+
":#{receiver.source}"
|
102
|
+
else
|
103
|
+
":#{receiver.method_name}"
|
104
|
+
end
|
105
|
+
end
|
91
106
|
|
92
|
-
|
107
|
+
def build_delegation(node, receiver)
|
108
|
+
delegation = ["delegate :#{node.body.method_name}", "to: #{receiver}"]
|
109
|
+
delegation << ['prefix: true'] if node.method?(prefixed_method_name(node.body))
|
110
|
+
delegation.join(', ')
|
111
|
+
end
|
112
|
+
|
113
|
+
def full_const_name(node)
|
114
|
+
return unless node.const_type?
|
115
|
+
unless node.namespace
|
116
|
+
return node.absolute? ? "::#{node.source}" : node.source
|
93
117
|
end
|
118
|
+
|
119
|
+
"#{full_const_name(node.namespace)}::#{node.short_name}"
|
94
120
|
end
|
95
121
|
|
96
122
|
def trivial_delegate?(def_node)
|
@@ -120,13 +146,30 @@ module RuboCop
|
|
120
146
|
def prefixed_method_name(body)
|
121
147
|
return '' if body.receiver.self_type?
|
122
148
|
|
123
|
-
[body.receiver
|
149
|
+
[determine_prefixed_method_receiver_name(body.receiver), body.method_name].join('_').to_sym
|
150
|
+
end
|
151
|
+
|
152
|
+
def determine_prefixed_method_receiver_name(receiver)
|
153
|
+
case receiver.type
|
154
|
+
when :cvar, :gvar, :ivar
|
155
|
+
receiver.source
|
156
|
+
when :const
|
157
|
+
full_const_name(receiver)
|
158
|
+
else
|
159
|
+
receiver.method_name.to_s
|
160
|
+
end
|
124
161
|
end
|
125
162
|
|
126
163
|
def private_or_protected_delegation(node)
|
127
164
|
private_or_protected_inline(node) || node_visibility(node) != :public
|
128
165
|
end
|
129
166
|
|
167
|
+
def module_function_declared?(node)
|
168
|
+
node.each_ancestor(:module, :begin).any? do |ancestor|
|
169
|
+
ancestor.children.any? { |child| child.send_type? && child.method?(:module_function) }
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
130
173
|
def private_or_protected_inline(node)
|
131
174
|
processed_source[node.first_line - 1].strip.match?(/\A(private )|(protected )/)
|
132
175
|
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
|
@@ -6,6 +6,9 @@ module RuboCop
|
|
6
6
|
# Identifies usages of file path joining process to use `Rails.root.join` clause.
|
7
7
|
# It is used to add uniformity when joining paths.
|
8
8
|
#
|
9
|
+
# NOTE: This cop ignores leading slashes in string literal arguments for `Rails.root.join`
|
10
|
+
# and multiple slashes in string literal arguments for `Rails.root.join` and `File.join`.
|
11
|
+
#
|
9
12
|
# @example EnforcedStyle: slashes (default)
|
10
13
|
# # bad
|
11
14
|
# Rails.root.join('app', 'models', 'goober')
|
@@ -66,6 +69,8 @@ module RuboCop
|
|
66
69
|
|
67
70
|
def on_send(node)
|
68
71
|
check_for_file_join_with_rails_root(node)
|
72
|
+
return unless node.receiver
|
73
|
+
|
69
74
|
check_for_rails_root_join_with_slash_separated_path(node)
|
70
75
|
check_for_rails_root_join_with_string_arguments(node)
|
71
76
|
end
|
@@ -76,6 +81,7 @@ module RuboCop
|
|
76
81
|
rails_root_index = find_rails_root_index(node)
|
77
82
|
slash_node = node.children[rails_root_index + 1]
|
78
83
|
return unless slash_node&.str_type? && slash_node.source.start_with?(File::SEPARATOR)
|
84
|
+
return unless node.children[rails_root_index].children.first.send_type?
|
79
85
|
|
80
86
|
register_offense(node, require_to_s: false) do |corrector|
|
81
87
|
autocorrect_slash_after_rails_root_in_dstr(corrector, node, rails_root_index)
|
@@ -94,7 +100,7 @@ module RuboCop
|
|
94
100
|
|
95
101
|
def check_for_file_join_with_rails_root(node)
|
96
102
|
return unless file_join_nodes?(node)
|
97
|
-
return unless node.arguments
|
103
|
+
return unless valid_arguments_for_file_join_with_rails_root?(node.arguments)
|
98
104
|
|
99
105
|
register_offense(node, require_to_s: true) do |corrector|
|
100
106
|
autocorrect_file_join(corrector, node) unless node.first_argument.array_type?
|
@@ -105,8 +111,7 @@ module RuboCop
|
|
105
111
|
return unless style == :slashes
|
106
112
|
return unless rails_root_nodes?(node)
|
107
113
|
return unless rails_root_join_nodes?(node)
|
108
|
-
return unless node.arguments
|
109
|
-
return unless node.arguments.all?(&:str_type?)
|
114
|
+
return unless valid_string_arguments_for_rails_root_join?(node.arguments)
|
110
115
|
|
111
116
|
register_offense(node, require_to_s: false) do |corrector|
|
112
117
|
autocorrect_rails_root_join_with_string_arguments(corrector, node)
|
@@ -117,15 +122,42 @@ module RuboCop
|
|
117
122
|
return unless style == :arguments
|
118
123
|
return unless rails_root_nodes?(node)
|
119
124
|
return unless rails_root_join_nodes?(node)
|
120
|
-
return unless node.arguments
|
125
|
+
return unless valid_slash_separated_path_for_rails_root_join?(node.arguments)
|
121
126
|
|
122
127
|
register_offense(node, require_to_s: false) do |corrector|
|
123
128
|
autocorrect_rails_root_join_with_slash_separated_path(corrector, node)
|
124
129
|
end
|
125
130
|
end
|
126
131
|
|
127
|
-
def
|
128
|
-
|
132
|
+
def valid_arguments_for_file_join_with_rails_root?(arguments)
|
133
|
+
return false unless arguments.any? { |arg| rails_root_nodes?(arg) }
|
134
|
+
|
135
|
+
arguments.none? { |arg| arg.variable? || arg.const_type? || string_contains_multiple_slashes?(arg) }
|
136
|
+
end
|
137
|
+
|
138
|
+
def valid_string_arguments_for_rails_root_join?(arguments)
|
139
|
+
return false unless arguments.size > 1
|
140
|
+
return false unless arguments.all?(&:str_type?)
|
141
|
+
|
142
|
+
arguments.none? { |arg| string_with_leading_slash?(arg) || string_contains_multiple_slashes?(arg) }
|
143
|
+
end
|
144
|
+
|
145
|
+
def valid_slash_separated_path_for_rails_root_join?(arguments)
|
146
|
+
return false unless arguments.any? { |arg| string_contains_slash?(arg) }
|
147
|
+
|
148
|
+
arguments.none? { |arg| string_with_leading_slash?(arg) || string_contains_multiple_slashes?(arg) }
|
149
|
+
end
|
150
|
+
|
151
|
+
def string_contains_slash?(node)
|
152
|
+
node.str_type? && node.value.include?(File::SEPARATOR)
|
153
|
+
end
|
154
|
+
|
155
|
+
def string_contains_multiple_slashes?(node)
|
156
|
+
node.str_type? && node.value.include?('//')
|
157
|
+
end
|
158
|
+
|
159
|
+
def string_with_leading_slash?(node)
|
160
|
+
node.str_type? && node.value.start_with?(File::SEPARATOR)
|
129
161
|
end
|
130
162
|
|
131
163
|
def register_offense(node, require_to_s:, &block)
|
@@ -170,7 +202,17 @@ module RuboCop
|
|
170
202
|
end
|
171
203
|
|
172
204
|
def autocorrect_file_join(corrector, node)
|
205
|
+
replace_receiver_with_rails_root(corrector, node)
|
206
|
+
remove_first_argument_with_comma(corrector, node)
|
207
|
+
process_arguments(corrector, node.arguments)
|
208
|
+
append_to_string_conversion(corrector, node)
|
209
|
+
end
|
210
|
+
|
211
|
+
def replace_receiver_with_rails_root(corrector, node)
|
173
212
|
corrector.replace(node.receiver, 'Rails.root')
|
213
|
+
end
|
214
|
+
|
215
|
+
def remove_first_argument_with_comma(corrector, node)
|
174
216
|
corrector.remove(
|
175
217
|
range_with_surrounding_space(
|
176
218
|
range_with_surrounding_comma(
|
@@ -180,9 +222,19 @@ module RuboCop
|
|
180
222
|
side: :right
|
181
223
|
)
|
182
224
|
)
|
183
|
-
|
184
|
-
|
225
|
+
end
|
226
|
+
|
227
|
+
def process_arguments(corrector, arguments)
|
228
|
+
arguments.each do |argument|
|
229
|
+
if argument.str_type?
|
230
|
+
corrector.replace(argument, argument.value.delete_prefix('/').inspect)
|
231
|
+
elsif argument.array_type?
|
232
|
+
corrector.replace(argument, "*#{argument.source}")
|
233
|
+
end
|
185
234
|
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def append_to_string_conversion(corrector, node)
|
186
238
|
corrector.insert_after(node, '.to_s')
|
187
239
|
end
|
188
240
|
|
@@ -203,7 +255,7 @@ module RuboCop
|
|
203
255
|
|
204
256
|
def autocorrect_rails_root_join_with_slash_separated_path(corrector, node)
|
205
257
|
node.arguments.each do |argument|
|
206
|
-
next unless
|
258
|
+
next unless string_contains_slash?(argument)
|
207
259
|
|
208
260
|
index = argument.source.index(File::SEPARATOR)
|
209
261
|
rest = inner_range_of(argument).adjust(begin_pos: index - 1)
|
@@ -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)
|