rubocop-rails 2.25.1 → 2.32.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 +60 -8
- data/config/default.yml +103 -51
- data/lib/rubocop/cop/mixin/active_record_helper.rb +2 -2
- data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +2 -2
- data/lib/rubocop/cop/mixin/database_type_resolvable.rb +2 -2
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +6 -1
- data/lib/rubocop/cop/mixin/index_method.rb +69 -61
- 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/action_order.rb +1 -5
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +1 -5
- data/lib/rubocop/cop/rails/add_column_index.rb +1 -0
- data/lib/rubocop/cop/rails/application_record.rb +4 -0
- data/lib/rubocop/cop/rails/arel_star.rb +5 -5
- 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 +3 -2
- data/lib/rubocop/cop/rails/compact_blank.rb +29 -8
- 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/date.rb +2 -2
- data/lib/rubocop/cop/rails/delegate.rb +53 -7
- data/lib/rubocop/cop/rails/duplicate_association.rb +8 -4
- data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +1 -3
- data/lib/rubocop/cop/rails/enum_hash.rb +31 -8
- data/lib/rubocop/cop/rails/enum_syntax.rb +130 -0
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +29 -7
- data/lib/rubocop/cop/rails/env_local.rb +26 -3
- data/lib/rubocop/cop/rails/file_path.rb +62 -10
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +7 -0
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +1 -1
- data/lib/rubocop/cop/rails/index_by.rb +37 -12
- data/lib/rubocop/cop/rails/index_with.rb +37 -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/output.rb +1 -2
- data/lib/rubocop/cop/rails/pluck.rb +30 -4
- data/lib/rubocop/cop/rails/pluck_in_where.rb +17 -8
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +30 -16
- data/lib/rubocop/cop/rails/presence.rb +1 -1
- data/lib/rubocop/cop/rails/present.rb +1 -3
- data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +1 -30
- data/lib/rubocop/cop/rails/redundant_foreign_key.rb +1 -1
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +9 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +7 -2
- data/lib/rubocop/cop/rails/reflection_class_name.rb +3 -3
- data/lib/rubocop/cop/rails/relative_date_constant.rb +1 -1
- data/lib/rubocop/cop/rails/render_plain_text.rb +6 -3
- data/lib/rubocop/cop/rails/request_referer.rb +1 -1
- data/lib/rubocop/cop/rails/reversible_migration.rb +4 -1
- data/lib/rubocop/cop/rails/root_pathname_methods.rb +21 -12
- data/lib/rubocop/cop/rails/save_bang.rb +8 -7
- data/lib/rubocop/cop/rails/schema_comment.rb +2 -1
- data/lib/rubocop/cop/rails/select_map.rb +3 -2
- data/lib/rubocop/cop/rails/skips_model_validations.rb +5 -3
- data/lib/rubocop/cop/rails/squished_sql_heredocs.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 +3 -2
- data/lib/rubocop/cop/rails/time_zone.rb +16 -7
- data/lib/rubocop/cop/rails/transaction_exit_statement.rb +7 -2
- 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/validation.rb +1 -1
- data/lib/rubocop/cop/rails/where_equals.rb +28 -12
- data/lib/rubocop/cop/rails/where_not.rb +11 -6
- data/lib/rubocop/cop/rails/where_range.rb +7 -2
- data/lib/rubocop/cop/rails_cops.rb +4 -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 -5
- metadata +29 -12
- data/lib/rubocop/rails/inject.rb +0 -18
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Looks for enums written with keyword arguments syntax.
|
7
|
+
#
|
8
|
+
# Defining enums with keyword arguments syntax is deprecated and will be removed in Rails 8.0.
|
9
|
+
# Positional arguments should be used instead:
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# # bad
|
13
|
+
# enum status: { active: 0, archived: 1 }, _prefix: true
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# enum :status, { active: 0, archived: 1 }, prefix: true
|
17
|
+
#
|
18
|
+
class EnumSyntax < Base
|
19
|
+
extend AutoCorrector
|
20
|
+
extend TargetRubyVersion
|
21
|
+
extend TargetRailsVersion
|
22
|
+
|
23
|
+
minimum_target_ruby_version 3.0
|
24
|
+
minimum_target_rails_version 7.0
|
25
|
+
|
26
|
+
MSG = 'Enum defined with keyword arguments in `%<enum>s` enum declaration. Use positional arguments instead.'
|
27
|
+
MSG_OPTIONS = 'Enum defined with deprecated options in `%<enum>s` enum declaration. Remove the `_` prefix.'
|
28
|
+
RESTRICT_ON_SEND = %i[enum].freeze
|
29
|
+
|
30
|
+
# From https://github.com/rails/rails/blob/v7.2.1/activerecord/lib/active_record/enum.rb#L231
|
31
|
+
OPTION_NAMES = %w[prefix suffix scopes default instance_methods].freeze
|
32
|
+
UNDERSCORED_OPTION_NAMES = OPTION_NAMES.map { |option| "_#{option}" }.freeze
|
33
|
+
|
34
|
+
def_node_matcher :enum?, <<~PATTERN
|
35
|
+
(send nil? :enum (hash $...))
|
36
|
+
PATTERN
|
37
|
+
|
38
|
+
def_node_matcher :enum_with_options?, <<~PATTERN
|
39
|
+
(send nil? :enum $_ ${array hash} $_)
|
40
|
+
PATTERN
|
41
|
+
|
42
|
+
def on_send(node)
|
43
|
+
check_and_correct_keyword_args(node)
|
44
|
+
check_enum_options(node)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def check_and_correct_keyword_args(node)
|
50
|
+
enum?(node) do |pairs|
|
51
|
+
pairs.each do |pair|
|
52
|
+
next if option_key?(pair)
|
53
|
+
|
54
|
+
correct_keyword_args(node, pair.key, pair.value, pairs[1..])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def check_enum_options(node)
|
60
|
+
enum_with_options?(node) do |key, _, options|
|
61
|
+
options.children.each do |option|
|
62
|
+
next unless option_key?(option)
|
63
|
+
|
64
|
+
add_offense(option.key, message: format(MSG_OPTIONS, enum: enum_name_value(key))) do |corrector|
|
65
|
+
corrector.replace(option.key, option.key.source.delete_prefix('_'))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def correct_keyword_args(node, key, values, options)
|
72
|
+
add_offense(values, message: format(MSG, enum: enum_name_value(key))) do |corrector|
|
73
|
+
# TODO: Multi-line autocorrect could be implemented in the future.
|
74
|
+
next if multiple_enum_definitions?(node)
|
75
|
+
|
76
|
+
preferred_syntax = "enum #{enum_name(key)}, #{values.source}#{correct_options(options)}"
|
77
|
+
|
78
|
+
corrector.replace(node, preferred_syntax)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def multiple_enum_definitions?(node)
|
83
|
+
keys = node.first_argument.keys.map { |key| key.source.delete_prefix('_') }
|
84
|
+
filterred_keys = keys.filter { |key| !OPTION_NAMES.include?(key) }
|
85
|
+
filterred_keys.size >= 2
|
86
|
+
end
|
87
|
+
|
88
|
+
def enum_name_value(key)
|
89
|
+
case key.type
|
90
|
+
when :sym, :str
|
91
|
+
key.value
|
92
|
+
else
|
93
|
+
key.source
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def enum_name(elem)
|
98
|
+
case elem.type
|
99
|
+
when :str
|
100
|
+
elem.value.dump
|
101
|
+
when :sym
|
102
|
+
elem.value.inspect
|
103
|
+
else
|
104
|
+
elem.source
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def option_key?(pair)
|
109
|
+
return false unless pair.respond_to?(:key)
|
110
|
+
|
111
|
+
UNDERSCORED_OPTION_NAMES.include?(pair.key.source)
|
112
|
+
end
|
113
|
+
|
114
|
+
def correct_options(options)
|
115
|
+
corrected_options = options.map do |pair|
|
116
|
+
name = if pair.key.source[0] == '_'
|
117
|
+
pair.key.source[1..]
|
118
|
+
else
|
119
|
+
pair.key.source
|
120
|
+
end
|
121
|
+
|
122
|
+
"#{name}: #{pair.value.source}"
|
123
|
+
end.join(', ')
|
124
|
+
|
125
|
+
", #{corrected_options}" unless corrected_options.empty?
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -7,6 +7,18 @@ module RuboCop
|
|
7
7
|
#
|
8
8
|
# @example
|
9
9
|
# # bad
|
10
|
+
# enum :status, { active: 0, archived: 0 }
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# enum :status, { active: 0, archived: 1 }
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# enum :status, [:active, :archived, :active]
|
17
|
+
#
|
18
|
+
# # good
|
19
|
+
# enum :status, [:active, :archived]
|
20
|
+
#
|
21
|
+
# # bad
|
10
22
|
# enum status: { active: 0, archived: 0 }
|
11
23
|
#
|
12
24
|
# # good
|
@@ -24,6 +36,10 @@ module RuboCop
|
|
24
36
|
RESTRICT_ON_SEND = %i[enum].freeze
|
25
37
|
|
26
38
|
def_node_matcher :enum?, <<~PATTERN
|
39
|
+
(send nil? :enum $_ ${array hash} ...)
|
40
|
+
PATTERN
|
41
|
+
|
42
|
+
def_node_matcher :enum_with_old_syntax?, <<~PATTERN
|
27
43
|
(send nil? :enum (hash $...))
|
28
44
|
PATTERN
|
29
45
|
|
@@ -32,15 +48,17 @@ module RuboCop
|
|
32
48
|
PATTERN
|
33
49
|
|
34
50
|
def on_send(node)
|
35
|
-
enum?(node) do |
|
51
|
+
enum?(node) do |key, args|
|
52
|
+
consecutive_duplicates(args.values).each do |item|
|
53
|
+
add_offense(item, message: message(key, item))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
enum_with_old_syntax?(node) do |pairs|
|
36
58
|
pairs.each do |pair|
|
37
59
|
enum_values(pair) do |key, args|
|
38
|
-
|
39
|
-
|
40
|
-
next unless duplicates?(items)
|
41
|
-
|
42
|
-
consecutive_duplicates(items).each do |item|
|
43
|
-
add_offense(item, message: format(MSG, value: item.source, enum: enum_name(key)))
|
60
|
+
consecutive_duplicates(args.values).each do |item|
|
61
|
+
add_offense(item, message: message(key, item))
|
44
62
|
end
|
45
63
|
end
|
46
64
|
end
|
@@ -57,6 +75,10 @@ module RuboCop
|
|
57
75
|
key.source
|
58
76
|
end
|
59
77
|
end
|
78
|
+
|
79
|
+
def message(key, item)
|
80
|
+
format(MSG, value: item.source, enum: enum_name(key))
|
81
|
+
end
|
60
82
|
end
|
61
83
|
end
|
62
84
|
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
|
@@ -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,10 +100,10 @@ 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
|
-
autocorrect_file_join(corrector, node)
|
106
|
+
autocorrect_file_join(corrector, node) unless node.first_argument.array_type?
|
101
107
|
end
|
102
108
|
end
|
103
109
|
|
@@ -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)
|
@@ -29,18 +29,34 @@ 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
|
+
(itblock
|
41
|
+
(call _ :to_h) $:it
|
42
|
+
(array $_ (lvar :it)))
|
43
|
+
}
|
36
44
|
PATTERN
|
37
45
|
|
38
46
|
def_node_matcher :on_bad_map_to_h, <<~PATTERN
|
39
47
|
(call
|
40
|
-
|
41
|
-
(
|
42
|
-
|
43
|
-
|
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
|
+
(itblock
|
57
|
+
(call _ {:map :collect}) $:it
|
58
|
+
(array $_ (lvar :it)))
|
59
|
+
}
|
44
60
|
:to_h)
|
45
61
|
PATTERN
|
46
62
|
|
@@ -48,10 +64,19 @@ module RuboCop
|
|
48
64
|
(send
|
49
65
|
(const {nil? cbase} :Hash)
|
50
66
|
:[]
|
51
|
-
|
52
|
-
(
|
53
|
-
|
54
|
-
|
67
|
+
{
|
68
|
+
(block
|
69
|
+
(call _ {:map :collect})
|
70
|
+
(args (arg $_el))
|
71
|
+
(array $_ (lvar _el)))
|
72
|
+
(numblock
|
73
|
+
(call _ {:map :collect}) $1
|
74
|
+
(array $_ (lvar :_1)))
|
75
|
+
(itblock
|
76
|
+
(call _ {:map :collect}) $:it
|
77
|
+
(array $_ (lvar :it)))
|
78
|
+
}
|
79
|
+
)
|
55
80
|
PATTERN
|
56
81
|
|
57
82
|
private
|
@@ -32,18 +32,34 @@ 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
|
+
(itblock
|
44
|
+
(call _ :to_h) $:it
|
45
|
+
(array (lvar :it) $_))
|
46
|
+
}
|
39
47
|
PATTERN
|
40
48
|
|
41
49
|
def_node_matcher :on_bad_map_to_h, <<~PATTERN
|
42
50
|
(call
|
43
|
-
|
44
|
-
(
|
45
|
-
|
46
|
-
|
51
|
+
{
|
52
|
+
(block
|
53
|
+
(call _ {:map :collect})
|
54
|
+
(args (arg $_el))
|
55
|
+
(array (lvar _el) $_))
|
56
|
+
(numblock
|
57
|
+
(call _ {:map :collect}) $1
|
58
|
+
(array (lvar :_1) $_))
|
59
|
+
(itblock
|
60
|
+
(call _ {:map :collect}) $:it
|
61
|
+
(array (lvar :it) $_))
|
62
|
+
}
|
47
63
|
:to_h)
|
48
64
|
PATTERN
|
49
65
|
|
@@ -51,10 +67,19 @@ module RuboCop
|
|
51
67
|
(send
|
52
68
|
(const {nil? cbase} :Hash)
|
53
69
|
:[]
|
54
|
-
|
55
|
-
(
|
56
|
-
|
57
|
-
|
70
|
+
{
|
71
|
+
(block
|
72
|
+
(call _ {:map :collect})
|
73
|
+
(args (arg $_el))
|
74
|
+
(array (lvar _el) $_))
|
75
|
+
(numblock
|
76
|
+
(call _ {:map :collect}) $1
|
77
|
+
(array (lvar :_1) $_))
|
78
|
+
(itblock
|
79
|
+
(call _ {:map :collect}) $:it
|
80
|
+
(array (lvar :it) $_))
|
81
|
+
}
|
82
|
+
)
|
58
83
|
PATTERN
|
59
84
|
|
60
85
|
private
|
@@ -29,7 +29,7 @@ module RuboCop
|
|
29
29
|
def on_send(node)
|
30
30
|
return unless node.arguments.empty?
|
31
31
|
return unless (receiver = node.receiver)
|
32
|
-
return
|
32
|
+
return unless receiver.type?(:str, :array)
|
33
33
|
|
34
34
|
add_offense(node.loc.selector)
|
35
35
|
end
|
@@ -115,6 +115,10 @@ module RuboCop
|
|
115
115
|
$_)))
|
116
116
|
PATTERN
|
117
117
|
|
118
|
+
def_node_matcher :delegated_methods, <<~PATTERN
|
119
|
+
(send nil? :delegate (sym $_)+ (hash <(pair (sym :to) _) ...>))
|
120
|
+
PATTERN
|
121
|
+
|
118
122
|
def on_send(node)
|
119
123
|
methods_node = only_or_except_filter_methods(node)
|
120
124
|
return unless methods_node
|
@@ -139,7 +143,13 @@ module RuboCop
|
|
139
143
|
return [] unless block
|
140
144
|
|
141
145
|
defined_methods = block.each_child_node(:def).map(&:method_name)
|
142
|
-
defined_methods + aliased_action_methods(block, defined_methods)
|
146
|
+
defined_methods + delegated_action_methods(block) + aliased_action_methods(block, defined_methods)
|
147
|
+
end
|
148
|
+
|
149
|
+
def delegated_action_methods(node)
|
150
|
+
node.each_child_node(:send).flat_map do |child_node|
|
151
|
+
delegated_methods(child_node) || []
|
152
|
+
end
|
143
153
|
end
|
144
154
|
|
145
155
|
def aliased_action_methods(node, defined_methods)
|
@@ -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
|