rubocop-rails 2.20.2 → 2.25.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/README.md +9 -7
- data/config/default.yml +81 -12
- data/lib/rubocop/cop/mixin/active_record_helper.rb +15 -3
- data/lib/rubocop/cop/mixin/database_type_resolvable.rb +66 -0
- data/lib/rubocop/cop/mixin/index_method.rb +2 -2
- data/lib/rubocop/cop/mixin/target_rails_version.rb +29 -2
- data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +3 -1
- data/lib/rubocop/cop/rails/action_controller_test_case.rb +2 -2
- data/lib/rubocop/cop/rails/action_filter.rb +3 -0
- data/lib/rubocop/cop/rails/active_record_aliases.rb +2 -2
- data/lib/rubocop/cop/rails/active_support_aliases.rb +6 -5
- data/lib/rubocop/cop/rails/active_support_on_load.rb +21 -1
- data/lib/rubocop/cop/rails/after_commit_override.rb +1 -1
- data/lib/rubocop/cop/rails/bulk_change_table.rb +8 -41
- data/lib/rubocop/cop/rails/content_tag.rb +1 -1
- data/lib/rubocop/cop/rails/dangerous_column_names.rb +446 -0
- data/lib/rubocop/cop/rails/date.rb +1 -1
- data/lib/rubocop/cop/rails/duplicate_association.rb +69 -12
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +3 -3
- data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +2 -2
- data/lib/rubocop/cop/rails/env_local.rb +46 -0
- data/lib/rubocop/cop/rails/expanded_date_range.rb +1 -1
- data/lib/rubocop/cop/rails/file_path.rb +9 -6
- data/lib/rubocop/cop/rails/find_by.rb +3 -3
- data/lib/rubocop/cop/rails/find_by_id.rb +9 -23
- data/lib/rubocop/cop/rails/freeze_time.rb +1 -1
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +1 -1
- data/lib/rubocop/cop/rails/helper_instance_variable.rb +1 -1
- data/lib/rubocop/cop/rails/http_status.rb +16 -5
- data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +63 -13
- data/lib/rubocop/cop/rails/inquiry.rb +1 -0
- data/lib/rubocop/cop/rails/inverse_of.rb +1 -1
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +7 -8
- data/lib/rubocop/cop/rails/not_null_column.rb +94 -6
- data/lib/rubocop/cop/rails/output.rb +3 -2
- data/lib/rubocop/cop/rails/pick.rb +10 -5
- data/lib/rubocop/cop/rails/pluck.rb +1 -1
- data/lib/rubocop/cop/rails/pluck_id.rb +2 -1
- data/lib/rubocop/cop/rails/pluck_in_where.rb +18 -5
- data/lib/rubocop/cop/rails/rake_environment.rb +22 -6
- data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +219 -0
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +7 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +1 -1
- data/lib/rubocop/cop/rails/response_parsed_body.rb +52 -10
- data/lib/rubocop/cop/rails/reversible_migration.rb +4 -4
- data/lib/rubocop/cop/rails/root_pathname_methods.rb +38 -4
- data/lib/rubocop/cop/rails/save_bang.rb +15 -8
- 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/skips_model_validations.rb +1 -1
- data/lib/rubocop/cop/rails/time_zone.rb +13 -5
- data/lib/rubocop/cop/rails/transaction_exit_statement.rb +29 -10
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +12 -4
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +2 -2
- data/lib/rubocop/cop/rails/unknown_env.rb +5 -1
- data/lib/rubocop/cop/rails/unused_ignored_columns.rb +6 -0
- data/lib/rubocop/cop/rails/unused_render_content.rb +67 -0
- data/lib/rubocop/cop/rails/validation.rb +6 -4
- data/lib/rubocop/cop/rails/where_equals.rb +3 -2
- data/lib/rubocop/cop/rails/where_exists.rb +9 -9
- data/lib/rubocop/cop/rails/where_missing.rb +6 -2
- data/lib/rubocop/cop/rails/where_not.rb +8 -6
- data/lib/rubocop/cop/rails/where_range.rb +157 -0
- data/lib/rubocop/cop/rails_cops.rb +7 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +3 -2
- data/lib/rubocop/rails/schema_loader.rb +5 -15
- data/lib/rubocop/rails/version.rb +1 -1
- data/lib/rubocop-rails.rb +8 -0
- metadata +31 -4
@@ -163,9 +163,9 @@ module RuboCop
|
|
163
163
|
|
164
164
|
def autocorrect_extension_after_rails_root_join_in_dstr(corrector, node, rails_root_index, extension_node)
|
165
165
|
rails_root_node = node.children[rails_root_index].children.first
|
166
|
-
return unless rails_root_node.
|
166
|
+
return unless rails_root_node.last_argument.str_type?
|
167
167
|
|
168
|
-
corrector.insert_before(rails_root_node.
|
168
|
+
corrector.insert_before(rails_root_node.last_argument.location.end, extension_node.source)
|
169
169
|
corrector.remove(extension_node)
|
170
170
|
end
|
171
171
|
|
@@ -174,17 +174,20 @@ module RuboCop
|
|
174
174
|
corrector.remove(
|
175
175
|
range_with_surrounding_space(
|
176
176
|
range_with_surrounding_comma(
|
177
|
-
node.
|
177
|
+
node.first_argument.source_range,
|
178
178
|
:right
|
179
179
|
),
|
180
180
|
side: :right
|
181
181
|
)
|
182
182
|
)
|
183
|
+
node.arguments.filter(&:str_type?).each do |argument|
|
184
|
+
corrector.replace(argument, argument.value.delete_prefix('/').inspect)
|
185
|
+
end
|
183
186
|
corrector.insert_after(node, '.to_s')
|
184
187
|
end
|
185
188
|
|
186
189
|
def autocorrect_rails_root_join_with_string_arguments(corrector, node)
|
187
|
-
corrector.replace(node.
|
190
|
+
corrector.replace(node.first_argument, %("#{node.arguments.map(&:value).join('/')}"))
|
188
191
|
node.arguments[1..].each do |argument|
|
189
192
|
corrector.remove(
|
190
193
|
range_with_surrounding_comma(
|
@@ -218,7 +221,7 @@ module RuboCop
|
|
218
221
|
end
|
219
222
|
|
220
223
|
def append_argument(corrector, node, argument_source)
|
221
|
-
corrector.insert_after(node.
|
224
|
+
corrector.insert_after(node.last_argument, %(, "#{argument_source}"))
|
222
225
|
end
|
223
226
|
|
224
227
|
def replace_with_rails_root_join(corrector, node, argument_source)
|
@@ -230,7 +233,7 @@ module RuboCop
|
|
230
233
|
end
|
231
234
|
|
232
235
|
def extension_node?(node)
|
233
|
-
node&.str_type? && node.source.
|
236
|
+
node&.str_type? && node.source.match?(/\A\.[A-Za-z]+/)
|
234
237
|
end
|
235
238
|
end
|
236
239
|
end
|
@@ -28,7 +28,7 @@ module RuboCop
|
|
28
28
|
include RangeHelp
|
29
29
|
extend AutoCorrector
|
30
30
|
|
31
|
-
MSG = 'Use `find_by` instead of `where
|
31
|
+
MSG = 'Use `find_by` instead of `where%<dot>s%<method>s`.'
|
32
32
|
RESTRICT_ON_SEND = %i[first take].freeze
|
33
33
|
|
34
34
|
def on_send(node)
|
@@ -37,7 +37,7 @@ module RuboCop
|
|
37
37
|
|
38
38
|
range = offense_range(node)
|
39
39
|
|
40
|
-
add_offense(range, message: format(MSG, method: node.method_name)) do |corrector|
|
40
|
+
add_offense(range, message: format(MSG, dot: node.loc.dot.source, method: node.method_name)) do |corrector|
|
41
41
|
autocorrect(corrector, node)
|
42
42
|
end
|
43
43
|
end
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
59
59
|
return if node.method?(:first)
|
60
60
|
|
61
61
|
where_loc = node.receiver.loc.selector
|
62
|
-
first_loc = range_between(node.
|
62
|
+
first_loc = range_between(node.receiver.source_range.end_pos, node.loc.selector.end_pos)
|
63
63
|
|
64
64
|
corrector.replace(where_loc, 'find_by')
|
65
65
|
corrector.replace(first_loc, '')
|
@@ -24,40 +24,39 @@ module RuboCop
|
|
24
24
|
RESTRICT_ON_SEND = %i[take! find_by_id! find_by!].freeze
|
25
25
|
|
26
26
|
def_node_matcher :where_take?, <<~PATTERN
|
27
|
-
(
|
28
|
-
$(
|
27
|
+
(call
|
28
|
+
$(call _ :where
|
29
29
|
(hash
|
30
30
|
(pair (sym :id) $_))) :take!)
|
31
31
|
PATTERN
|
32
32
|
|
33
33
|
def_node_matcher :find_by?, <<~PATTERN
|
34
34
|
{
|
35
|
-
(
|
36
|
-
(
|
35
|
+
(call _ :find_by_id! $_)
|
36
|
+
(call _ :find_by! (hash (pair (sym :id) $_)))
|
37
37
|
}
|
38
38
|
PATTERN
|
39
39
|
|
40
40
|
def on_send(node)
|
41
41
|
where_take?(node) do |where, id_value|
|
42
42
|
range = where_take_offense_range(node, where)
|
43
|
-
bad_method = build_where_take_bad_method(id_value)
|
44
43
|
|
45
|
-
register_offense(range, id_value
|
44
|
+
register_offense(range, id_value)
|
46
45
|
end
|
47
46
|
|
48
47
|
find_by?(node) do |id_value|
|
49
48
|
range = find_by_offense_range(node)
|
50
|
-
bad_method = build_find_by_bad_method(node, id_value)
|
51
49
|
|
52
|
-
register_offense(range, id_value
|
50
|
+
register_offense(range, id_value)
|
53
51
|
end
|
54
52
|
end
|
53
|
+
alias on_csend on_send
|
55
54
|
|
56
55
|
private
|
57
56
|
|
58
|
-
def register_offense(range, id_value
|
57
|
+
def register_offense(range, id_value)
|
59
58
|
good_method = build_good_method(id_value)
|
60
|
-
message = format(MSG, good_method: good_method, bad_method:
|
59
|
+
message = format(MSG, good_method: good_method, bad_method: range.source)
|
61
60
|
|
62
61
|
add_offense(range, message: message) do |corrector|
|
63
62
|
corrector.replace(range, good_method)
|
@@ -75,19 +74,6 @@ module RuboCop
|
|
75
74
|
def build_good_method(id_value)
|
76
75
|
"find(#{id_value.source})"
|
77
76
|
end
|
78
|
-
|
79
|
-
def build_where_take_bad_method(id_value)
|
80
|
-
"where(id: #{id_value.source}).take!"
|
81
|
-
end
|
82
|
-
|
83
|
-
def build_find_by_bad_method(node, id_value)
|
84
|
-
case node.method_name
|
85
|
-
when :find_by_id!
|
86
|
-
"find_by_id!(#{id_value.source})"
|
87
|
-
when :find_by!
|
88
|
-
"find_by!(id: #{id_value.source})"
|
89
|
-
end
|
90
|
-
end
|
91
77
|
end
|
92
78
|
end
|
93
79
|
end
|
@@ -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
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
6
6
|
# Checks for use of the helper methods which reference
|
7
7
|
# instance variables.
|
8
8
|
#
|
9
|
-
# Relying on instance variables makes it difficult to
|
9
|
+
# Relying on instance variables makes it difficult to reuse helper
|
10
10
|
# methods.
|
11
11
|
#
|
12
12
|
# If it seems awkward to explicitly pass in each dependent
|
@@ -8,10 +8,13 @@ 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
|
14
15
|
# head 200
|
16
|
+
# assert_response 200
|
17
|
+
# assert_redirected_to '/some/path', status: 301
|
15
18
|
#
|
16
19
|
# # good
|
17
20
|
# render :foo, status: :ok
|
@@ -19,6 +22,8 @@ module RuboCop
|
|
19
22
|
# render plain: 'foo/bar', status: :not_modified
|
20
23
|
# redirect_to root_url, status: :moved_permanently
|
21
24
|
# head :ok
|
25
|
+
# assert_response :ok
|
26
|
+
# assert_redirected_to '/some/path', status: :moved_permanently
|
22
27
|
#
|
23
28
|
# @example EnforcedStyle: numeric
|
24
29
|
# # bad
|
@@ -27,6 +32,8 @@ module RuboCop
|
|
27
32
|
# render plain: 'foo/bar', status: :not_modified
|
28
33
|
# redirect_to root_url, status: :moved_permanently
|
29
34
|
# head :ok
|
35
|
+
# assert_response :ok
|
36
|
+
# assert_redirected_to '/some/path', status: :moved_permanently
|
30
37
|
#
|
31
38
|
# # good
|
32
39
|
# render :foo, status: 200
|
@@ -34,23 +41,27 @@ module RuboCop
|
|
34
41
|
# render plain: 'foo/bar', status: 304
|
35
42
|
# redirect_to root_url, status: 301
|
36
43
|
# head 200
|
44
|
+
# assert_response 200
|
45
|
+
# assert_redirected_to '/some/path', status: 301
|
37
46
|
#
|
38
47
|
class HttpStatus < Base
|
39
48
|
include ConfigurableEnforcedStyle
|
40
49
|
extend AutoCorrector
|
41
50
|
|
42
|
-
RESTRICT_ON_SEND = %i[render redirect_to head].freeze
|
51
|
+
RESTRICT_ON_SEND = %i[render redirect_to head assert_response assert_redirected_to].freeze
|
43
52
|
|
44
53
|
def_node_matcher :http_status, <<~PATTERN
|
45
54
|
{
|
46
55
|
(send nil? {:render :redirect_to} _ $hash)
|
47
56
|
(send nil? {:render :redirect_to} $hash)
|
48
|
-
(send nil? :head ${int sym} ...)
|
57
|
+
(send nil? {:head :assert_response} ${int sym} ...)
|
58
|
+
(send nil? :assert_redirected_to _ $hash ...)
|
59
|
+
(send nil? :assert_redirected_to $hash ...)
|
49
60
|
}
|
50
61
|
PATTERN
|
51
62
|
|
52
63
|
def_node_matcher :status_code, <<~PATTERN
|
53
|
-
(hash <(pair (sym :status) ${int sym}) ...>)
|
64
|
+
(hash <(pair (sym :status) ${int sym str}) ...>)
|
54
65
|
PATTERN
|
55
66
|
|
56
67
|
def on_send(node)
|
@@ -108,7 +119,7 @@ module RuboCop
|
|
108
119
|
private
|
109
120
|
|
110
121
|
def symbol
|
111
|
-
::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number)
|
122
|
+
::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number.to_i)
|
112
123
|
end
|
113
124
|
|
114
125
|
def number
|
@@ -133,7 +144,7 @@ module RuboCop
|
|
133
144
|
end
|
134
145
|
|
135
146
|
def offensive?
|
136
|
-
!node.int_type? && !permitted_symbol?
|
147
|
+
!node.int_type? && !permitted_symbol? && number
|
137
148
|
end
|
138
149
|
|
139
150
|
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
|
@@ -222,7 +222,7 @@ module RuboCop
|
|
222
222
|
|
223
223
|
def with_options_arguments(recv, node)
|
224
224
|
blocks = node.each_ancestor(:block).select do |block|
|
225
|
-
block.send_node.command?(:with_options) && same_context_in_with_options?(block.
|
225
|
+
block.send_node.command?(:with_options) && same_context_in_with_options?(block.first_argument, recv)
|
226
226
|
end
|
227
227
|
blocks.flat_map { |n| n.send_node.arguments }
|
228
228
|
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
|
|
@@ -3,20 +3,45 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
# Checks for add_column
|
7
|
-
#
|
6
|
+
# Checks for add_column calls with a NOT NULL constraint without a default
|
7
|
+
# value.
|
8
|
+
#
|
9
|
+
# This cop only applies when adding a column to an existing table, since
|
10
|
+
# existing records will not have a value for the new column. New tables
|
11
|
+
# can freely use NOT NULL columns without defaults, since there are no
|
12
|
+
# records that could violate the constraint.
|
13
|
+
#
|
14
|
+
# If you need to add a NOT NULL column to an existing table, you must add
|
15
|
+
# it as nullable first, back-fill the data, and then use
|
16
|
+
# `change_column_null`. Alternatively, you could add the column with a
|
17
|
+
# default first to have the database automatically backfill existing rows,
|
18
|
+
# and then use `change_column_default` to remove the default.
|
19
|
+
#
|
20
|
+
# `TEXT` cannot have a default value in MySQL.
|
21
|
+
# The cop will automatically detect an adapter from `development`
|
22
|
+
# environment in `config/database.yml` or the environment variable
|
23
|
+
# `DATABASE_URL` when the `Database` option is not set. If the database
|
24
|
+
# is MySQL, this cop ignores offenses for `TEXT` columns.
|
8
25
|
#
|
9
26
|
# @example
|
10
27
|
# # bad
|
11
28
|
# add_column :users, :name, :string, null: false
|
12
29
|
# add_reference :products, :category, null: false
|
30
|
+
# change_table :users do |t|
|
31
|
+
# t.string :name, null: false
|
32
|
+
# end
|
13
33
|
#
|
14
34
|
# # good
|
15
35
|
# add_column :users, :name, :string, null: true
|
16
36
|
# add_column :users, :name, :string, null: false, default: ''
|
37
|
+
# change_table :users do |t|
|
38
|
+
# t.string :name, null: false, default: ''
|
39
|
+
# end
|
17
40
|
# add_reference :products, :category
|
18
|
-
#
|
41
|
+
# change_column_null :products, :category_id, false
|
19
42
|
class NotNullColumn < Base
|
43
|
+
include DatabaseTypeResolvable
|
44
|
+
|
20
45
|
MSG = 'Do not add a NOT NULL column without a default value.'
|
21
46
|
RESTRICT_ON_SEND = %i[add_column add_reference].freeze
|
22
47
|
|
@@ -28,6 +53,22 @@ module RuboCop
|
|
28
53
|
(send nil? :add_reference _ _ (hash $...))
|
29
54
|
PATTERN
|
30
55
|
|
56
|
+
def_node_matcher :change_table?, <<~PATTERN
|
57
|
+
(block (send nil? :change_table ...) (args (arg $_)) _)
|
58
|
+
PATTERN
|
59
|
+
|
60
|
+
def_node_matcher :add_not_null_column_in_change_table?, <<~PATTERN
|
61
|
+
(send (lvar $_) :column _ $_ (hash $...))
|
62
|
+
PATTERN
|
63
|
+
|
64
|
+
def_node_matcher :add_not_null_column_via_shortcut_in_change_table?, <<~PATTERN
|
65
|
+
(send (lvar $_) $_ _ (hash $...))
|
66
|
+
PATTERN
|
67
|
+
|
68
|
+
def_node_matcher :add_not_null_reference_in_change_table?, <<~PATTERN
|
69
|
+
(send (lvar $_) :add_reference _ _ (hash $...))
|
70
|
+
PATTERN
|
71
|
+
|
31
72
|
def_node_matcher :null_false?, <<~PATTERN
|
32
73
|
(pair (sym :null) (false))
|
33
74
|
PATTERN
|
@@ -41,13 +82,25 @@ module RuboCop
|
|
41
82
|
check_add_reference(node)
|
42
83
|
end
|
43
84
|
|
85
|
+
def on_block(node)
|
86
|
+
check_change_table(node)
|
87
|
+
end
|
88
|
+
alias on_numblock on_block
|
89
|
+
|
44
90
|
private
|
45
91
|
|
92
|
+
def check_column(type, pairs)
|
93
|
+
if type.respond_to?(:value)
|
94
|
+
return if type.value == :virtual || type.value == 'virtual'
|
95
|
+
return if (type.value == :text || type.value == 'text') && database == MYSQL
|
96
|
+
end
|
97
|
+
|
98
|
+
check_pairs(pairs)
|
99
|
+
end
|
100
|
+
|
46
101
|
def check_add_column(node)
|
47
102
|
add_not_null_column?(node) do |type, pairs|
|
48
|
-
|
49
|
-
|
50
|
-
check_pairs(pairs)
|
103
|
+
check_column(type, pairs)
|
51
104
|
end
|
52
105
|
end
|
53
106
|
|
@@ -57,6 +110,41 @@ module RuboCop
|
|
57
110
|
end
|
58
111
|
end
|
59
112
|
|
113
|
+
def check_add_column_in_change_table(node, table)
|
114
|
+
add_not_null_column_in_change_table?(node) do |receiver, type, pairs|
|
115
|
+
next unless receiver == table
|
116
|
+
|
117
|
+
check_column(type, pairs)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def check_add_column_via_shortcut_in_change_table(node, table)
|
122
|
+
add_not_null_column_via_shortcut_in_change_table?(node) do |receiver, type, pairs|
|
123
|
+
next unless receiver == table
|
124
|
+
|
125
|
+
check_column(type, pairs)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def check_add_reference_in_change_table(node, table)
|
130
|
+
add_not_null_reference_in_change_table?(node) do |receiver, pairs|
|
131
|
+
next unless receiver == table
|
132
|
+
|
133
|
+
check_pairs(pairs)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def check_change_table(node)
|
138
|
+
change_table?(node) do |table|
|
139
|
+
children = node.body.begin_type? ? node.body.children : [node.body]
|
140
|
+
children.each do |child|
|
141
|
+
check_add_column_in_change_table(child, table)
|
142
|
+
check_add_column_via_shortcut_in_change_table(child, table)
|
143
|
+
check_add_reference_in_change_table(child, table)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
60
148
|
def check_pairs(pairs)
|
61
149
|
return if pairs.any? { |pair| default_option?(pair) }
|
62
150
|
|
@@ -23,6 +23,7 @@ module RuboCop
|
|
23
23
|
|
24
24
|
MSG = "Do not write to stdout. Use Rails's logger if you want to log."
|
25
25
|
RESTRICT_ON_SEND = %i[ap p pp pretty_print print puts binwrite syswrite write write_nonblock].freeze
|
26
|
+
ALLOWED_TYPES = %i[send csend block numblock].freeze
|
26
27
|
|
27
28
|
def_node_matcher :output?, <<~PATTERN
|
28
29
|
(send nil? {:ap :p :pp :pretty_print :print :puts} ...)
|
@@ -39,8 +40,8 @@ module RuboCop
|
|
39
40
|
PATTERN
|
40
41
|
|
41
42
|
def on_send(node)
|
42
|
-
return if node.parent&.
|
43
|
-
return
|
43
|
+
return if ALLOWED_TYPES.include?(node.parent&.type)
|
44
|
+
return if !output?(node) && !io_output?(node)
|
44
45
|
|
45
46
|
range = offense_range(node)
|
46
47
|
|
@@ -9,6 +9,10 @@ module RuboCop
|
|
9
9
|
# `pick` avoids. When called on an Active Record relation, `pick` adds a
|
10
10
|
# limit to the query so that only one value is fetched from the database.
|
11
11
|
#
|
12
|
+
# Note that when `pick` is added to a relation with an existing limit, it
|
13
|
+
# causes a subquery to be added. In most cases this is undesirable, and
|
14
|
+
# care should be taken while resolving this violation.
|
15
|
+
#
|
12
16
|
# @safety
|
13
17
|
# This cop is unsafe because `pluck` is defined on both `ActiveRecord::Relation` and `Enumerable`,
|
14
18
|
# whereas `pick` is only defined on `ActiveRecord::Relation` in Rails 6.0. This was addressed
|
@@ -28,13 +32,13 @@ module RuboCop
|
|
28
32
|
extend AutoCorrector
|
29
33
|
extend TargetRailsVersion
|
30
34
|
|
31
|
-
MSG = 'Prefer `pick(%<args>s)` over
|
35
|
+
MSG = 'Prefer `pick(%<args>s)` over `%<current>s`.'
|
32
36
|
RESTRICT_ON_SEND = %i[first].freeze
|
33
37
|
|
34
38
|
minimum_target_rails_version 6.0
|
35
39
|
|
36
40
|
def_node_matcher :pick_candidate?, <<~PATTERN
|
37
|
-
(
|
41
|
+
(call (call _ :pluck ...) :first)
|
38
42
|
PATTERN
|
39
43
|
|
40
44
|
def on_send(node)
|
@@ -44,7 +48,7 @@ module RuboCop
|
|
44
48
|
node_selector = node.loc.selector
|
45
49
|
range = receiver_selector.join(node_selector)
|
46
50
|
|
47
|
-
add_offense(range, message: message(receiver)) do |corrector|
|
51
|
+
add_offense(range, message: message(receiver, range)) do |corrector|
|
48
52
|
first_range = receiver.source_range.end.join(node_selector)
|
49
53
|
|
50
54
|
corrector.remove(first_range)
|
@@ -52,11 +56,12 @@ module RuboCop
|
|
52
56
|
end
|
53
57
|
end
|
54
58
|
end
|
59
|
+
alias on_csend on_send
|
55
60
|
|
56
61
|
private
|
57
62
|
|
58
|
-
def message(receiver)
|
59
|
-
format(MSG, args: receiver.arguments.map(&:source).join(', '))
|
63
|
+
def message(receiver, current)
|
64
|
+
format(MSG, args: receiver.arguments.map(&:source).join(', '), current: current.source)
|
60
65
|
end
|
61
66
|
end
|
62
67
|
end
|
@@ -38,7 +38,7 @@ module RuboCop
|
|
38
38
|
minimum_target_rails_version 5.0
|
39
39
|
|
40
40
|
def_node_matcher :pluck_candidate?, <<~PATTERN
|
41
|
-
({block numblock} (
|
41
|
+
({block numblock} (call _ {:map :collect}) $_argument (send lvar :[] $_key))
|
42
42
|
PATTERN
|
43
43
|
|
44
44
|
def on_block(node)
|
@@ -34,7 +34,7 @@ module RuboCop
|
|
34
34
|
RESTRICT_ON_SEND = %i[pluck].freeze
|
35
35
|
|
36
36
|
def_node_matcher :pluck_id_call?, <<~PATTERN
|
37
|
-
(
|
37
|
+
(call _ :pluck {(sym :id) (send nil? :primary_key)})
|
38
38
|
PATTERN
|
39
39
|
|
40
40
|
def on_send(node)
|
@@ -47,6 +47,7 @@ module RuboCop
|
|
47
47
|
corrector.replace(offense_range(node), 'ids')
|
48
48
|
end
|
49
49
|
end
|
50
|
+
alias on_csend on_send
|
50
51
|
|
51
52
|
private
|
52
53
|
|