rubocop-rails 2.19.1 → 2.21.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/README.md +3 -1
- data/config/default.yml +86 -12
- data/lib/rubocop/cop/mixin/index_method.rb +2 -2
- data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +1 -1
- data/lib/rubocop/cop/rails/action_controller_test_case.rb +2 -2
- data/lib/rubocop/cop/rails/assert_not.rb +0 -1
- data/lib/rubocop/cop/rails/bulk_change_table.rb +20 -3
- data/lib/rubocop/cop/rails/dangerous_column_names.rb +439 -0
- data/lib/rubocop/cop/rails/date.rb +12 -3
- data/lib/rubocop/cop/rails/duplicate_association.rb +3 -0
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +3 -3
- data/lib/rubocop/cop/rails/file_path.rb +129 -13
- data/lib/rubocop/cop/rails/find_each.rb +1 -1
- data/lib/rubocop/cop/rails/freeze_time.rb +1 -1
- data/lib/rubocop/cop/rails/http_status.rb +4 -3
- data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +63 -13
- data/lib/rubocop/cop/rails/i18n_locale_texts.rb +5 -1
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +22 -2
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +9 -10
- data/lib/rubocop/cop/rails/not_null_column.rb +1 -1
- data/lib/rubocop/cop/rails/rake_environment.rb +20 -4
- data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +168 -0
- data/lib/rubocop/cop/rails/refute_methods.rb +0 -1
- data/lib/rubocop/cop/rails/reversible_migration.rb +1 -1
- data/lib/rubocop/cop/rails/root_pathname_methods.rb +38 -4
- data/lib/rubocop/cop/rails/save_bang.rb +2 -2
- data/lib/rubocop/cop/rails/schema_comment.rb +16 -10
- data/lib/rubocop/cop/rails/select_map.rb +78 -0
- data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +0 -1
- data/lib/rubocop/cop/rails/three_state_boolean_column.rb +2 -4
- data/lib/rubocop/cop/rails/time_zone.rb +12 -5
- data/lib/rubocop/cop/rails/transaction_exit_statement.rb +35 -9
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +15 -19
- data/lib/rubocop/cop/rails/unused_render_content.rb +67 -0
- data/lib/rubocop/cop/rails/where_exists.rb +0 -1
- data/lib/rubocop/cop/rails_cops.rb +4 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +4 -4
- data/lib/rubocop/rails/schema_loader.rb +1 -1
- data/lib/rubocop/rails/version.rb +1 -1
- data/lib/rubocop-rails.rb +8 -0
- metadata +8 -4
@@ -35,6 +35,8 @@ module RuboCop
|
|
35
35
|
# Rails.root.join('app', 'models', 'goober').to_s
|
36
36
|
#
|
37
37
|
class FilePath < Base
|
38
|
+
extend AutoCorrector
|
39
|
+
|
38
40
|
include ConfigurableEnforcedStyle
|
39
41
|
include RangeHelp
|
40
42
|
|
@@ -56,13 +58,10 @@ module RuboCop
|
|
56
58
|
|
57
59
|
def on_dstr(node)
|
58
60
|
return unless rails_root_nodes?(node)
|
59
|
-
return
|
60
|
-
|
61
|
-
last_child_source = node.children.last.source
|
62
|
-
return unless last_child_source.start_with?('.') || last_child_source.include?(File::SEPARATOR)
|
63
|
-
return if last_child_source.start_with?(':')
|
61
|
+
return if dstr_separated_by_colon?(node)
|
64
62
|
|
65
|
-
|
63
|
+
check_for_slash_after_rails_root_in_dstr(node)
|
64
|
+
check_for_extension_after_rails_root_join_in_dstr(node)
|
66
65
|
end
|
67
66
|
|
68
67
|
def on_send(node)
|
@@ -73,11 +72,33 @@ module RuboCop
|
|
73
72
|
|
74
73
|
private
|
75
74
|
|
75
|
+
def check_for_slash_after_rails_root_in_dstr(node)
|
76
|
+
rails_root_index = find_rails_root_index(node)
|
77
|
+
slash_node = node.children[rails_root_index + 1]
|
78
|
+
return unless slash_node&.str_type? && slash_node.source.start_with?(File::SEPARATOR)
|
79
|
+
|
80
|
+
register_offense(node, require_to_s: false) do |corrector|
|
81
|
+
autocorrect_slash_after_rails_root_in_dstr(corrector, node, rails_root_index)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def check_for_extension_after_rails_root_join_in_dstr(node)
|
86
|
+
rails_root_index = find_rails_root_index(node)
|
87
|
+
extension_node = node.children[rails_root_index + 1]
|
88
|
+
return unless extension_node?(extension_node)
|
89
|
+
|
90
|
+
register_offense(node, require_to_s: false) do |corrector|
|
91
|
+
autocorrect_extension_after_rails_root_join_in_dstr(corrector, node, rails_root_index, extension_node)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
76
95
|
def check_for_file_join_with_rails_root(node)
|
77
96
|
return unless file_join_nodes?(node)
|
78
97
|
return unless node.arguments.any? { |e| rails_root_nodes?(e) }
|
79
98
|
|
80
|
-
register_offense(node, require_to_s: true)
|
99
|
+
register_offense(node, require_to_s: true) do |corrector|
|
100
|
+
autocorrect_file_join(corrector, node)
|
101
|
+
end
|
81
102
|
end
|
82
103
|
|
83
104
|
def check_for_rails_root_join_with_string_arguments(node)
|
@@ -87,7 +108,9 @@ module RuboCop
|
|
87
108
|
return unless node.arguments.size > 1
|
88
109
|
return unless node.arguments.all?(&:str_type?)
|
89
110
|
|
90
|
-
register_offense(node, require_to_s: false)
|
111
|
+
register_offense(node, require_to_s: false) do |corrector|
|
112
|
+
autocorrect_rails_root_join_with_string_arguments(corrector, node)
|
113
|
+
end
|
91
114
|
end
|
92
115
|
|
93
116
|
def check_for_rails_root_join_with_slash_separated_path(node)
|
@@ -96,21 +119,22 @@ module RuboCop
|
|
96
119
|
return unless rails_root_join_nodes?(node)
|
97
120
|
return unless node.arguments.any? { |arg| string_with_slash?(arg) }
|
98
121
|
|
99
|
-
register_offense(node, require_to_s: false)
|
122
|
+
register_offense(node, require_to_s: false) do |corrector|
|
123
|
+
autocorrect_rails_root_join_with_slash_separated_path(corrector, node)
|
124
|
+
end
|
100
125
|
end
|
101
126
|
|
102
127
|
def string_with_slash?(node)
|
103
|
-
node.str_type? && node.source.include?(
|
128
|
+
node.str_type? && node.source.include?(File::SEPARATOR)
|
104
129
|
end
|
105
130
|
|
106
|
-
def register_offense(node, require_to_s
|
131
|
+
def register_offense(node, require_to_s:, &block)
|
107
132
|
line_range = node.loc.column...node.loc.last_column
|
108
133
|
source_range = source_range(processed_source.buffer, node.first_line, line_range)
|
109
|
-
require_to_s = false if node.dstr_type?
|
110
134
|
|
111
135
|
message = build_message(require_to_s)
|
112
136
|
|
113
|
-
add_offense(source_range, message: message)
|
137
|
+
add_offense(source_range, message: message, &block)
|
114
138
|
end
|
115
139
|
|
116
140
|
def build_message(require_to_s)
|
@@ -119,6 +143,98 @@ module RuboCop
|
|
119
143
|
|
120
144
|
format(message_template, to_s: to_s)
|
121
145
|
end
|
146
|
+
|
147
|
+
def dstr_separated_by_colon?(node)
|
148
|
+
node.children[1..].any? do |child|
|
149
|
+
child.str_type? && child.source.start_with?(':')
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def autocorrect_slash_after_rails_root_in_dstr(corrector, node, rails_root_index)
|
154
|
+
rails_root_node = node.children[rails_root_index].children.first
|
155
|
+
argument_source = extract_rails_root_join_argument_source(node, rails_root_index)
|
156
|
+
if rails_root_node.method?(:join)
|
157
|
+
append_argument(corrector, rails_root_node, argument_source)
|
158
|
+
else
|
159
|
+
replace_with_rails_root_join(corrector, rails_root_node, argument_source)
|
160
|
+
end
|
161
|
+
node.children[rails_root_index + 1..].each { |child| corrector.remove(child) }
|
162
|
+
end
|
163
|
+
|
164
|
+
def autocorrect_extension_after_rails_root_join_in_dstr(corrector, node, rails_root_index, extension_node)
|
165
|
+
rails_root_node = node.children[rails_root_index].children.first
|
166
|
+
return unless rails_root_node.arguments.last.str_type?
|
167
|
+
|
168
|
+
corrector.insert_before(rails_root_node.arguments.last.location.end, extension_node.source)
|
169
|
+
corrector.remove(extension_node)
|
170
|
+
end
|
171
|
+
|
172
|
+
def autocorrect_file_join(corrector, node)
|
173
|
+
corrector.replace(node.receiver, 'Rails.root')
|
174
|
+
corrector.remove(
|
175
|
+
range_with_surrounding_space(
|
176
|
+
range_with_surrounding_comma(
|
177
|
+
node.arguments.first.source_range,
|
178
|
+
:right
|
179
|
+
),
|
180
|
+
side: :right
|
181
|
+
)
|
182
|
+
)
|
183
|
+
node.arguments.filter(&:str_type?).each do |argument|
|
184
|
+
corrector.replace(argument, argument.value.delete_prefix('/').inspect)
|
185
|
+
end
|
186
|
+
corrector.insert_after(node, '.to_s')
|
187
|
+
end
|
188
|
+
|
189
|
+
def autocorrect_rails_root_join_with_string_arguments(corrector, node)
|
190
|
+
corrector.replace(node.arguments.first, %("#{node.arguments.map(&:value).join('/')}"))
|
191
|
+
node.arguments[1..].each do |argument|
|
192
|
+
corrector.remove(
|
193
|
+
range_with_surrounding_comma(
|
194
|
+
range_with_surrounding_space(
|
195
|
+
argument.source_range,
|
196
|
+
side: :left
|
197
|
+
),
|
198
|
+
:left
|
199
|
+
)
|
200
|
+
)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def autocorrect_rails_root_join_with_slash_separated_path(corrector, node)
|
205
|
+
node.arguments.each do |argument|
|
206
|
+
next unless string_with_slash?(argument)
|
207
|
+
|
208
|
+
index = argument.source.index(File::SEPARATOR)
|
209
|
+
rest = inner_range_of(argument).adjust(begin_pos: index - 1)
|
210
|
+
corrector.remove(rest)
|
211
|
+
corrector.insert_after(argument, %(, "#{rest.source.delete_prefix(File::SEPARATOR)}"))
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def inner_range_of(node)
|
216
|
+
node.location.end.with(begin_pos: node.location.begin.end_pos).adjust(end_pos: -1)
|
217
|
+
end
|
218
|
+
|
219
|
+
def find_rails_root_index(node)
|
220
|
+
node.children.index { |child| rails_root_nodes?(child) }
|
221
|
+
end
|
222
|
+
|
223
|
+
def append_argument(corrector, node, argument_source)
|
224
|
+
corrector.insert_after(node.arguments.last, %(, "#{argument_source}"))
|
225
|
+
end
|
226
|
+
|
227
|
+
def replace_with_rails_root_join(corrector, node, argument_source)
|
228
|
+
corrector.replace(node, %<Rails.root.join("#{argument_source}")>)
|
229
|
+
end
|
230
|
+
|
231
|
+
def extract_rails_root_join_argument_source(node, rails_root_index)
|
232
|
+
node.children[rails_root_index + 1..].map(&:source).join.delete_prefix(File::SEPARATOR)
|
233
|
+
end
|
234
|
+
|
235
|
+
def extension_node?(node)
|
236
|
+
node&.str_type? && node.source.match?(/\A\.[A-Za-z]+/)
|
237
|
+
end
|
122
238
|
end
|
123
239
|
end
|
124
240
|
end
|
@@ -34,7 +34,7 @@ module RuboCop
|
|
34
34
|
RESTRICT_ON_SEND = %i[each].freeze
|
35
35
|
|
36
36
|
SCOPE_METHODS = %i[
|
37
|
-
all eager_load includes joins left_joins left_outer_joins not preload
|
37
|
+
all eager_load includes joins left_joins left_outer_joins not or preload
|
38
38
|
references unscoped where
|
39
39
|
].freeze
|
40
40
|
|
@@ -69,7 +69,7 @@ module RuboCop
|
|
69
69
|
return false unless CONVERT_METHODS.include?(method_name)
|
70
70
|
|
71
71
|
child_node, child_method_name, time_argument = *node.children
|
72
|
-
return if time_argument
|
72
|
+
return false if time_argument
|
73
73
|
|
74
74
|
current_time?(child_node, child_method_name)
|
75
75
|
end
|
@@ -8,6 +8,7 @@ module RuboCop
|
|
8
8
|
# @example EnforcedStyle: symbolic (default)
|
9
9
|
# # bad
|
10
10
|
# render :foo, status: 200
|
11
|
+
# render :foo, status: '200'
|
11
12
|
# render json: { foo: 'bar' }, status: 200
|
12
13
|
# render plain: 'foo/bar', status: 304
|
13
14
|
# redirect_to root_url, status: 301
|
@@ -50,7 +51,7 @@ module RuboCop
|
|
50
51
|
PATTERN
|
51
52
|
|
52
53
|
def_node_matcher :status_code, <<~PATTERN
|
53
|
-
(hash <(pair (sym :status) ${int sym}) ...>)
|
54
|
+
(hash <(pair (sym :status) ${int sym str}) ...>)
|
54
55
|
PATTERN
|
55
56
|
|
56
57
|
def on_send(node)
|
@@ -108,7 +109,7 @@ module RuboCop
|
|
108
109
|
private
|
109
110
|
|
110
111
|
def symbol
|
111
|
-
::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number)
|
112
|
+
::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number.to_i)
|
112
113
|
end
|
113
114
|
|
114
115
|
def number
|
@@ -133,7 +134,7 @@ module RuboCop
|
|
133
134
|
end
|
134
135
|
|
135
136
|
def offensive?
|
136
|
-
!node.int_type? && !permitted_symbol?
|
137
|
+
!node.int_type? && !permitted_symbol? && number
|
137
138
|
end
|
138
139
|
|
139
140
|
def message
|
@@ -5,7 +5,13 @@ module RuboCop
|
|
5
5
|
module Rails
|
6
6
|
# Checks for places where I18n "lazy" lookup can be used.
|
7
7
|
#
|
8
|
-
#
|
8
|
+
# This cop has two different enforcement modes. When the EnforcedStyle
|
9
|
+
# is `lazy` (the default), explicit lookups are added as offenses.
|
10
|
+
#
|
11
|
+
# When the EnforcedStyle is `explicit` then lazy lookups are added as
|
12
|
+
# offenses.
|
13
|
+
#
|
14
|
+
# @example EnforcedStyle: lazy (default)
|
9
15
|
# # en.yml
|
10
16
|
# # en:
|
11
17
|
# # books:
|
@@ -28,11 +34,29 @@ module RuboCop
|
|
28
34
|
# end
|
29
35
|
# end
|
30
36
|
#
|
37
|
+
# @example EnforcedStyle: explicit
|
38
|
+
# # bad
|
39
|
+
# class BooksController < ApplicationController
|
40
|
+
# def create
|
41
|
+
# # ...
|
42
|
+
# redirect_to books_url, notice: t('.success')
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# # good
|
47
|
+
# class BooksController < ApplicationController
|
48
|
+
# def create
|
49
|
+
# # ...
|
50
|
+
# redirect_to books_url, notice: t('books.create.success')
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
31
54
|
class I18nLazyLookup < Base
|
55
|
+
include ConfigurableEnforcedStyle
|
32
56
|
include VisibilityHelp
|
33
57
|
extend AutoCorrector
|
34
58
|
|
35
|
-
MSG = 'Use
|
59
|
+
MSG = 'Use %<style>s lookup for the text used in controllers.'
|
36
60
|
|
37
61
|
RESTRICT_ON_SEND = %i[translate t].freeze
|
38
62
|
|
@@ -42,23 +66,45 @@ module RuboCop
|
|
42
66
|
|
43
67
|
def on_send(node)
|
44
68
|
translate_call?(node) do |key_node|
|
45
|
-
|
46
|
-
|
69
|
+
case style
|
70
|
+
when :lazy
|
71
|
+
handle_lazy_style(node, key_node)
|
72
|
+
when :explicit
|
73
|
+
handle_explicit_style(node, key_node)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
47
79
|
|
48
|
-
|
49
|
-
|
80
|
+
def handle_lazy_style(node, key_node)
|
81
|
+
key = key_node.value
|
82
|
+
return if key.to_s.start_with?('.')
|
50
83
|
|
51
|
-
|
52
|
-
|
84
|
+
controller, action = controller_and_action(node)
|
85
|
+
return unless controller && action
|
53
86
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
87
|
+
scoped_key = get_scoped_key(key_node, controller, action)
|
88
|
+
return unless key == scoped_key
|
89
|
+
|
90
|
+
add_offense(key_node) do |corrector|
|
91
|
+
unscoped_key = key_node.value.to_s.split('.').last
|
92
|
+
corrector.replace(key_node, "'.#{unscoped_key}'")
|
58
93
|
end
|
59
94
|
end
|
60
95
|
|
61
|
-
|
96
|
+
def handle_explicit_style(node, key_node)
|
97
|
+
key = key_node.value
|
98
|
+
return unless key.to_s.start_with?('.')
|
99
|
+
|
100
|
+
controller, action = controller_and_action(node)
|
101
|
+
return unless controller && action
|
102
|
+
|
103
|
+
scoped_key = get_scoped_key(key_node, controller, action)
|
104
|
+
add_offense(key_node) do |corrector|
|
105
|
+
corrector.replace(key_node, "'#{scoped_key}'")
|
106
|
+
end
|
107
|
+
end
|
62
108
|
|
63
109
|
def controller_and_action(node)
|
64
110
|
action_node = node.each_ancestor(:def).first
|
@@ -90,6 +136,10 @@ module RuboCop
|
|
90
136
|
|
91
137
|
path.delete_suffix('Controller').underscore
|
92
138
|
end
|
139
|
+
|
140
|
+
def message(_range)
|
141
|
+
format(MSG, style: style)
|
142
|
+
end
|
93
143
|
end
|
94
144
|
end
|
95
145
|
end
|
@@ -80,7 +80,11 @@ module RuboCop
|
|
80
80
|
PATTERN
|
81
81
|
|
82
82
|
def_node_matcher :flash_assignment?, <<~PATTERN
|
83
|
-
(send
|
83
|
+
(send
|
84
|
+
{
|
85
|
+
(send nil? :flash)
|
86
|
+
(send (send nil? :flash) :now)
|
87
|
+
} :[]= _ $str)
|
84
88
|
PATTERN
|
85
89
|
|
86
90
|
def_node_search :mail_subject, <<~PATTERN
|
@@ -36,6 +36,10 @@ module RuboCop
|
|
36
36
|
# if: -> { trusted_origin? && action_name != "admin" }
|
37
37
|
# end
|
38
38
|
class IgnoredSkipActionFilterOption < Base
|
39
|
+
extend AutoCorrector
|
40
|
+
|
41
|
+
include RangeHelp
|
42
|
+
|
39
43
|
MSG = <<~MSG.chomp.freeze
|
40
44
|
`%<ignore>s` option will be ignored when `%<prefer>s` and `%<ignore>s` are used together.
|
41
45
|
MSG
|
@@ -60,9 +64,13 @@ module RuboCop
|
|
60
64
|
options = options_hash(options)
|
61
65
|
|
62
66
|
if if_and_only?(options)
|
63
|
-
add_offense(options[:if], message: format(MSG, prefer: :only, ignore: :if))
|
67
|
+
add_offense(options[:if], message: format(MSG, prefer: :only, ignore: :if)) do |corrector|
|
68
|
+
remove_node_with_left_space_and_comma(corrector, options[:if])
|
69
|
+
end
|
64
70
|
elsif if_and_except?(options)
|
65
|
-
add_offense(options[:except], message: format(MSG, prefer: :if, ignore: :except))
|
71
|
+
add_offense(options[:except], message: format(MSG, prefer: :if, ignore: :except)) do |corrector|
|
72
|
+
remove_node_with_left_space_and_comma(corrector, options[:except])
|
73
|
+
end
|
66
74
|
end
|
67
75
|
end
|
68
76
|
|
@@ -81,6 +89,18 @@ module RuboCop
|
|
81
89
|
def if_and_except?(options)
|
82
90
|
options.key?(:if) && options.key?(:except)
|
83
91
|
end
|
92
|
+
|
93
|
+
def remove_node_with_left_space_and_comma(corrector, node)
|
94
|
+
corrector.remove(
|
95
|
+
range_with_surrounding_comma(
|
96
|
+
range_with_surrounding_space(
|
97
|
+
node.source_range,
|
98
|
+
side: :left
|
99
|
+
),
|
100
|
+
:left
|
101
|
+
)
|
102
|
+
)
|
103
|
+
end
|
84
104
|
end
|
85
105
|
end
|
86
106
|
end
|
@@ -122,24 +122,23 @@ module RuboCop
|
|
122
122
|
parent = node.each_ancestor(:class, :module).first
|
123
123
|
return unless parent
|
124
124
|
|
125
|
+
# NOTE: a `:begin` node may not exist if the class/module consists of a single statement
|
125
126
|
block = parent.each_child_node(:begin).first
|
126
|
-
return unless block
|
127
|
-
|
128
127
|
defined_action_methods = defined_action_methods(block)
|
129
128
|
|
130
|
-
|
131
|
-
|
132
|
-
end
|
129
|
+
unmatched_methods = array_values(methods_node) - defined_action_methods
|
130
|
+
return if unmatched_methods.empty?
|
133
131
|
|
134
|
-
message = message(
|
135
|
-
add_offense(node, message: message)
|
132
|
+
message = message(unmatched_methods, parent)
|
133
|
+
add_offense(node, message: message)
|
136
134
|
end
|
137
135
|
|
138
136
|
private
|
139
137
|
|
140
138
|
def defined_action_methods(block)
|
141
|
-
|
139
|
+
return [] unless block
|
142
140
|
|
141
|
+
defined_methods = block.each_child_node(:def).map(&:method_name)
|
143
142
|
defined_methods + aliased_action_methods(block, defined_methods)
|
144
143
|
end
|
145
144
|
|
@@ -176,14 +175,14 @@ module RuboCop
|
|
176
175
|
when :sym
|
177
176
|
[node.value]
|
178
177
|
when :array
|
179
|
-
node.values.
|
178
|
+
node.values.filter_map do |v|
|
180
179
|
case v.type
|
181
180
|
when :str
|
182
181
|
v.str_content.to_sym
|
183
182
|
when :sym
|
184
183
|
v.value
|
185
184
|
end
|
186
|
-
end
|
185
|
+
end
|
187
186
|
else
|
188
187
|
[]
|
189
188
|
end
|
@@ -45,7 +45,7 @@ module RuboCop
|
|
45
45
|
|
46
46
|
def check_add_column(node)
|
47
47
|
add_not_null_column?(node) do |type, pairs|
|
48
|
-
return if type.value == :virtual || type.value == 'virtual'
|
48
|
+
return if type.respond_to?(:value) && (type.value == :virtual || type.value == 'virtual')
|
49
49
|
|
50
50
|
check_pairs(pairs)
|
51
51
|
end
|
@@ -45,16 +45,24 @@ module RuboCop
|
|
45
45
|
return if with_dependencies?(task_method)
|
46
46
|
|
47
47
|
add_offense(task_method) do |corrector|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
if with_arguments?(task_method)
|
49
|
+
new_task_dependency = correct_task_arguments_dependency(task_method)
|
50
|
+
corrector.replace(task_arguments(task_method), new_task_dependency)
|
51
|
+
else
|
52
|
+
task_name = task_method.first_argument
|
53
|
+
new_task_dependency = correct_task_dependency(task_name)
|
54
|
+
corrector.replace(task_name, new_task_dependency)
|
55
|
+
end
|
52
56
|
end
|
53
57
|
end
|
54
58
|
end
|
55
59
|
|
56
60
|
private
|
57
61
|
|
62
|
+
def correct_task_arguments_dependency(task_method)
|
63
|
+
"#{task_arguments(task_method).source} => :environment"
|
64
|
+
end
|
65
|
+
|
58
66
|
def correct_task_dependency(task_name)
|
59
67
|
if task_name.sym_type?
|
60
68
|
"#{task_name.source.delete(':|\'|"')}: :environment"
|
@@ -80,6 +88,14 @@ module RuboCop
|
|
80
88
|
end
|
81
89
|
end
|
82
90
|
|
91
|
+
def task_arguments(node)
|
92
|
+
node.arguments[1]
|
93
|
+
end
|
94
|
+
|
95
|
+
def with_arguments?(node)
|
96
|
+
node.arguments.size > 1 && node.arguments[1].array_type?
|
97
|
+
end
|
98
|
+
|
83
99
|
def with_dependencies?(node)
|
84
100
|
first_arg = node.arguments[0]
|
85
101
|
return false unless first_arg
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Detect redundant `all` used as a receiver for Active Record query methods.
|
7
|
+
#
|
8
|
+
# @safety
|
9
|
+
# This cop is unsafe for autocorrection if the receiver for `all` is not an Active Record object.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# # bad
|
13
|
+
# User.all.find(id)
|
14
|
+
# User.all.order(:created_at)
|
15
|
+
# users.all.where(id: ids)
|
16
|
+
# user.articles.all.order(:created_at)
|
17
|
+
#
|
18
|
+
# # good
|
19
|
+
# User.find(id)
|
20
|
+
# User.order(:created_at)
|
21
|
+
# users.where(id: ids)
|
22
|
+
# user.articles.order(:created_at)
|
23
|
+
#
|
24
|
+
# @example AllowedReceivers: ['ActionMailer::Preview', 'ActiveSupport::TimeZone'] (default)
|
25
|
+
# # good
|
26
|
+
# ActionMailer::Preview.all.first
|
27
|
+
# ActiveSupport::TimeZone.all.first
|
28
|
+
class RedundantActiveRecordAllMethod < Base
|
29
|
+
include ActiveRecordHelper
|
30
|
+
include AllowedReceivers
|
31
|
+
include RangeHelp
|
32
|
+
extend AutoCorrector
|
33
|
+
|
34
|
+
MSG = 'Redundant `all` detected.'
|
35
|
+
|
36
|
+
RESTRICT_ON_SEND = [:all].freeze
|
37
|
+
|
38
|
+
# Defined methods in `ActiveRecord::Querying::QUERYING_METHODS` on activerecord 7.0.5.
|
39
|
+
QUERYING_METHODS = %i[
|
40
|
+
and
|
41
|
+
annotate
|
42
|
+
any?
|
43
|
+
average
|
44
|
+
calculate
|
45
|
+
count
|
46
|
+
create_or_find_by
|
47
|
+
create_or_find_by!
|
48
|
+
create_with
|
49
|
+
delete_all
|
50
|
+
delete_by
|
51
|
+
destroy_all
|
52
|
+
destroy_by
|
53
|
+
distinct
|
54
|
+
eager_load
|
55
|
+
except
|
56
|
+
excluding
|
57
|
+
exists?
|
58
|
+
extending
|
59
|
+
extract_associated
|
60
|
+
fifth
|
61
|
+
fifth!
|
62
|
+
find
|
63
|
+
find_by
|
64
|
+
find_by!
|
65
|
+
find_each
|
66
|
+
find_in_batches
|
67
|
+
find_or_create_by
|
68
|
+
find_or_create_by!
|
69
|
+
find_or_initialize_by
|
70
|
+
find_sole_by
|
71
|
+
first
|
72
|
+
first!
|
73
|
+
first_or_create
|
74
|
+
first_or_create!
|
75
|
+
first_or_initialize
|
76
|
+
forty_two
|
77
|
+
forty_two!
|
78
|
+
fourth
|
79
|
+
fourth!
|
80
|
+
from
|
81
|
+
group
|
82
|
+
having
|
83
|
+
ids
|
84
|
+
in_batches
|
85
|
+
in_order_of
|
86
|
+
includes
|
87
|
+
invert_where
|
88
|
+
joins
|
89
|
+
last
|
90
|
+
last!
|
91
|
+
left_joins
|
92
|
+
left_outer_joins
|
93
|
+
limit
|
94
|
+
lock
|
95
|
+
many?
|
96
|
+
maximum
|
97
|
+
merge
|
98
|
+
minimum
|
99
|
+
none
|
100
|
+
none?
|
101
|
+
offset
|
102
|
+
one?
|
103
|
+
only
|
104
|
+
optimizer_hints
|
105
|
+
or
|
106
|
+
order
|
107
|
+
pick
|
108
|
+
pluck
|
109
|
+
preload
|
110
|
+
readonly
|
111
|
+
references
|
112
|
+
reorder
|
113
|
+
reselect
|
114
|
+
rewhere
|
115
|
+
second
|
116
|
+
second!
|
117
|
+
second_to_last
|
118
|
+
second_to_last!
|
119
|
+
select
|
120
|
+
sole
|
121
|
+
strict_loading
|
122
|
+
sum
|
123
|
+
take
|
124
|
+
take!
|
125
|
+
third
|
126
|
+
third!
|
127
|
+
third_to_last
|
128
|
+
third_to_last!
|
129
|
+
touch_all
|
130
|
+
unscope
|
131
|
+
update_all
|
132
|
+
where
|
133
|
+
without
|
134
|
+
].to_set.freeze
|
135
|
+
|
136
|
+
POSSIBLE_ENUMERABLE_BLOCK_METHODS = %i[any? count find none? one? select sum].freeze
|
137
|
+
|
138
|
+
def_node_matcher :followed_by_query_method?, <<~PATTERN
|
139
|
+
(send (send _ :all) QUERYING_METHODS ...)
|
140
|
+
PATTERN
|
141
|
+
|
142
|
+
def on_send(node)
|
143
|
+
return if !followed_by_query_method?(node.parent) || possible_enumerable_block_method?(node)
|
144
|
+
return if node.receiver ? allowed_receiver?(node.receiver) : !inherit_active_record_base?(node)
|
145
|
+
|
146
|
+
range_of_all_method = offense_range(node)
|
147
|
+
add_offense(range_of_all_method) do |collector|
|
148
|
+
collector.remove(range_of_all_method)
|
149
|
+
collector.remove(node.parent.loc.dot)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def possible_enumerable_block_method?(node)
|
156
|
+
parent = node.parent
|
157
|
+
return false unless POSSIBLE_ENUMERABLE_BLOCK_METHODS.include?(parent.method_name)
|
158
|
+
|
159
|
+
parent.parent&.block_type? || parent.parent&.numblock_type? || parent.first_argument&.block_pass_type?
|
160
|
+
end
|
161
|
+
|
162
|
+
def offense_range(node)
|
163
|
+
range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|