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
@@ -22,10 +22,13 @@ module RuboCop
|
|
22
22
|
# @example
|
23
23
|
# # bad
|
24
24
|
# Post.where(user_id: User.active.pluck(:id))
|
25
|
+
# Post.where(user_id: User.active.ids)
|
26
|
+
# Post.where.not(user_id: User.active.pluck(:id))
|
25
27
|
#
|
26
28
|
# # good
|
27
29
|
# Post.where(user_id: User.active.select(:id))
|
28
30
|
# Post.where(user_id: active_users.select(:id))
|
31
|
+
# Post.where.not(user_id: active_users.select(:id))
|
29
32
|
#
|
30
33
|
# @example EnforcedStyle: conservative (default)
|
31
34
|
# # good
|
@@ -40,8 +43,9 @@ module RuboCop
|
|
40
43
|
include ConfigurableEnforcedStyle
|
41
44
|
extend AutoCorrector
|
42
45
|
|
43
|
-
|
44
|
-
|
46
|
+
MSG_SELECT = 'Use `select` instead of `pluck` within `where` query method.'
|
47
|
+
MSG_IDS = 'Use `select(:id)` instead of `ids` within `where` query method.'
|
48
|
+
RESTRICT_ON_SEND = %i[pluck ids].freeze
|
45
49
|
|
46
50
|
def on_send(node)
|
47
51
|
return unless in_where?(node)
|
@@ -49,17 +53,26 @@ module RuboCop
|
|
49
53
|
|
50
54
|
range = node.loc.selector
|
51
55
|
|
52
|
-
|
53
|
-
|
56
|
+
if node.method?(:ids)
|
57
|
+
replacement = 'select(:id)'
|
58
|
+
message = MSG_IDS
|
59
|
+
else
|
60
|
+
replacement = 'select'
|
61
|
+
message = MSG_SELECT
|
62
|
+
end
|
63
|
+
|
64
|
+
add_offense(range, message: message) do |corrector|
|
65
|
+
corrector.replace(range, replacement)
|
54
66
|
end
|
55
67
|
end
|
68
|
+
alias on_csend on_send
|
56
69
|
|
57
70
|
private
|
58
71
|
|
59
72
|
def root_receiver(node)
|
60
73
|
receiver = node.receiver
|
61
74
|
|
62
|
-
if receiver&.
|
75
|
+
if receiver&.call_type?
|
63
76
|
root_receiver(receiver)
|
64
77
|
else
|
65
78
|
receiver
|
@@ -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"
|
@@ -64,7 +72,7 @@ module RuboCop
|
|
64
72
|
end
|
65
73
|
|
66
74
|
def task_name(node)
|
67
|
-
first_arg = node.
|
75
|
+
first_arg = node.first_argument
|
68
76
|
case first_arg&.type
|
69
77
|
when :sym, :str
|
70
78
|
first_arg.value.to_sym
|
@@ -80,8 +88,16 @@ 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
|
-
first_arg = node.
|
100
|
+
first_arg = node.first_argument
|
85
101
|
return false unless first_arg
|
86
102
|
|
87
103
|
if first_arg.hash_type?
|
@@ -0,0 +1,219 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# TODO: In the future, please support only RuboCop 1.52+ and use `RuboCop::Cop::AllowedReceivers`:
|
7
|
+
# https://github.com/rubocop/rubocop/blob/v1.52.0/lib/rubocop/cop/mixin/allowed_receivers.rb
|
8
|
+
# At that time, this duplicated module implementation can be removed.
|
9
|
+
module AllowedReceivers
|
10
|
+
def allowed_receiver?(receiver)
|
11
|
+
receiver_name = receiver_name(receiver)
|
12
|
+
|
13
|
+
allowed_receivers.include?(receiver_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def receiver_name(receiver)
|
17
|
+
return receiver_name(receiver.receiver) if receiver.receiver && !receiver.receiver.const_type?
|
18
|
+
|
19
|
+
if receiver.send_type?
|
20
|
+
if receiver.receiver
|
21
|
+
"#{receiver_name(receiver.receiver)}.#{receiver.method_name}"
|
22
|
+
else
|
23
|
+
receiver.method_name.to_s
|
24
|
+
end
|
25
|
+
else
|
26
|
+
receiver.source
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def allowed_receivers
|
31
|
+
cop_config.fetch('AllowedReceivers', [])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Detect redundant `all` used as a receiver for Active Record query methods.
|
36
|
+
#
|
37
|
+
# For the methods `delete_all` and `destroy_all`, this cop will only check cases where the receiver is a model.
|
38
|
+
# It will ignore cases where the receiver is an association (e.g., `user.articles.all.delete_all`).
|
39
|
+
# This is because omitting `all` from an association changes the methods
|
40
|
+
# from `ActiveRecord::Relation` to `ActiveRecord::Associations::CollectionProxy`,
|
41
|
+
# which can affect their behavior.
|
42
|
+
#
|
43
|
+
# @safety
|
44
|
+
# This cop is unsafe for autocorrection if the receiver for `all` is not an Active Record object.
|
45
|
+
#
|
46
|
+
# @example
|
47
|
+
# # bad
|
48
|
+
# User.all.find(id)
|
49
|
+
# User.all.order(:created_at)
|
50
|
+
# users.all.where(id: ids)
|
51
|
+
# user.articles.all.order(:created_at)
|
52
|
+
#
|
53
|
+
# # good
|
54
|
+
# User.find(id)
|
55
|
+
# User.order(:created_at)
|
56
|
+
# users.where(id: ids)
|
57
|
+
# user.articles.order(:created_at)
|
58
|
+
#
|
59
|
+
# @example AllowedReceivers: ['ActionMailer::Preview', 'ActiveSupport::TimeZone'] (default)
|
60
|
+
# # good
|
61
|
+
# ActionMailer::Preview.all.first
|
62
|
+
# ActiveSupport::TimeZone.all.first
|
63
|
+
class RedundantActiveRecordAllMethod < Base
|
64
|
+
include ActiveRecordHelper
|
65
|
+
include AllowedReceivers
|
66
|
+
include RangeHelp
|
67
|
+
extend AutoCorrector
|
68
|
+
|
69
|
+
MSG = 'Redundant `all` detected.'
|
70
|
+
|
71
|
+
RESTRICT_ON_SEND = [:all].freeze
|
72
|
+
|
73
|
+
# Defined methods in `ActiveRecord::Querying::QUERYING_METHODS` on activerecord 7.1.0.
|
74
|
+
QUERYING_METHODS = %i[
|
75
|
+
and
|
76
|
+
annotate
|
77
|
+
any?
|
78
|
+
async_average
|
79
|
+
async_count
|
80
|
+
async_ids
|
81
|
+
async_maximum
|
82
|
+
async_minimum
|
83
|
+
async_pick
|
84
|
+
async_pluck
|
85
|
+
async_sum
|
86
|
+
average
|
87
|
+
calculate
|
88
|
+
count
|
89
|
+
create_or_find_by
|
90
|
+
create_or_find_by!
|
91
|
+
create_with
|
92
|
+
delete_all
|
93
|
+
delete_by
|
94
|
+
destroy_all
|
95
|
+
destroy_by
|
96
|
+
distinct
|
97
|
+
eager_load
|
98
|
+
except
|
99
|
+
excluding
|
100
|
+
exists?
|
101
|
+
extending
|
102
|
+
extract_associated
|
103
|
+
fifth
|
104
|
+
fifth!
|
105
|
+
find
|
106
|
+
find_by
|
107
|
+
find_by!
|
108
|
+
find_each
|
109
|
+
find_in_batches
|
110
|
+
find_or_create_by
|
111
|
+
find_or_create_by!
|
112
|
+
find_or_initialize_by
|
113
|
+
find_sole_by
|
114
|
+
first
|
115
|
+
first!
|
116
|
+
first_or_create
|
117
|
+
first_or_create!
|
118
|
+
first_or_initialize
|
119
|
+
forty_two
|
120
|
+
forty_two!
|
121
|
+
fourth
|
122
|
+
fourth!
|
123
|
+
from
|
124
|
+
group
|
125
|
+
having
|
126
|
+
ids
|
127
|
+
in_batches
|
128
|
+
in_order_of
|
129
|
+
includes
|
130
|
+
invert_where
|
131
|
+
joins
|
132
|
+
last
|
133
|
+
last!
|
134
|
+
left_joins
|
135
|
+
left_outer_joins
|
136
|
+
limit
|
137
|
+
lock
|
138
|
+
many?
|
139
|
+
maximum
|
140
|
+
merge
|
141
|
+
minimum
|
142
|
+
none
|
143
|
+
none?
|
144
|
+
offset
|
145
|
+
one?
|
146
|
+
only
|
147
|
+
optimizer_hints
|
148
|
+
or
|
149
|
+
order
|
150
|
+
pick
|
151
|
+
pluck
|
152
|
+
preload
|
153
|
+
readonly
|
154
|
+
references
|
155
|
+
regroup
|
156
|
+
reorder
|
157
|
+
reselect
|
158
|
+
rewhere
|
159
|
+
second
|
160
|
+
second!
|
161
|
+
second_to_last
|
162
|
+
second_to_last!
|
163
|
+
select
|
164
|
+
sole
|
165
|
+
strict_loading
|
166
|
+
sum
|
167
|
+
take
|
168
|
+
take!
|
169
|
+
third
|
170
|
+
third!
|
171
|
+
third_to_last
|
172
|
+
third_to_last!
|
173
|
+
touch_all
|
174
|
+
unscope
|
175
|
+
update_all
|
176
|
+
where
|
177
|
+
with
|
178
|
+
without
|
179
|
+
].to_set.freeze
|
180
|
+
|
181
|
+
POSSIBLE_ENUMERABLE_BLOCK_METHODS = %i[any? count find none? one? select sum].freeze
|
182
|
+
SENSITIVE_METHODS_ON_ASSOCIATION = %i[delete_all destroy_all].freeze
|
183
|
+
|
184
|
+
def_node_matcher :followed_by_query_method?, <<~PATTERN
|
185
|
+
(send (send _ :all) QUERYING_METHODS ...)
|
186
|
+
PATTERN
|
187
|
+
|
188
|
+
def on_send(node)
|
189
|
+
return unless followed_by_query_method?(node.parent)
|
190
|
+
return if possible_enumerable_block_method?(node) || sensitive_association_method?(node)
|
191
|
+
return if node.receiver ? allowed_receiver?(node.receiver) : !inherit_active_record_base?(node)
|
192
|
+
|
193
|
+
range_of_all_method = offense_range(node)
|
194
|
+
add_offense(range_of_all_method) do |collector|
|
195
|
+
collector.remove(range_of_all_method)
|
196
|
+
collector.remove(node.parent.loc.dot)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
|
202
|
+
def possible_enumerable_block_method?(node)
|
203
|
+
parent = node.parent
|
204
|
+
return false unless POSSIBLE_ENUMERABLE_BLOCK_METHODS.include?(parent.method_name)
|
205
|
+
|
206
|
+
parent.parent&.block_type? || parent.parent&.numblock_type? || parent.first_argument&.block_pass_type?
|
207
|
+
end
|
208
|
+
|
209
|
+
def sensitive_association_method?(node)
|
210
|
+
!node.receiver&.const_type? && SENSITIVE_METHODS_ON_ASSOCIATION.include?(node.parent.method_name)
|
211
|
+
end
|
212
|
+
|
213
|
+
def offense_range(node)
|
214
|
+
range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -53,6 +53,12 @@ module RuboCop
|
|
53
53
|
# @example source that matches - by a foreign key
|
54
54
|
# validates :user_id, presence: true
|
55
55
|
#
|
56
|
+
# @example source that DOES NOT match - if condition
|
57
|
+
# validates :user_id, presence: true, if: condition
|
58
|
+
#
|
59
|
+
# @example source that DOES NOT match - unless condition
|
60
|
+
# validates :user_id, presence: true, unless: condition
|
61
|
+
#
|
56
62
|
# @example source that DOES NOT match - strict validation
|
57
63
|
# validates :user_id, presence: true, strict: true
|
58
64
|
#
|
@@ -65,6 +71,7 @@ module RuboCop
|
|
65
71
|
$[
|
66
72
|
(hash <$(pair (sym :presence) true) ...>) # presence: true
|
67
73
|
!(hash <$(pair (sym :strict) {true const}) ...>) # strict: true
|
74
|
+
!(hash <$(pair (sym {:if :unless}) _) ...>) # if: some_condition or unless: some_condition
|
68
75
|
]
|
69
76
|
)
|
70
77
|
PATTERN
|
@@ -3,25 +3,30 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
# Prefer `response.parsed_body` to `
|
6
|
+
# Prefer `response.parsed_body` to custom parsing logic for `response.body`.
|
7
7
|
#
|
8
8
|
# @safety
|
9
|
-
# This cop is unsafe because Content-Type may not be `application/json
|
10
|
-
# Content-Type provided by corporate entities such as
|
11
|
-
# `
|
9
|
+
# This cop is unsafe because Content-Type may not be `application/json` or `text/html`.
|
10
|
+
# For example, the proprietary Content-Type provided by corporate entities such as
|
11
|
+
# `application/vnd.github+json` is not supported at `response.parsed_body` by default,
|
12
|
+
# so you still have to use `JSON.parse(response.body)` there.
|
12
13
|
#
|
13
14
|
# @example
|
14
15
|
# # bad
|
15
16
|
# JSON.parse(response.body)
|
16
17
|
#
|
18
|
+
# # bad
|
19
|
+
# Nokogiri::HTML.parse(response.body)
|
20
|
+
#
|
21
|
+
# # bad
|
22
|
+
# Nokogiri::HTML5.parse(response.body)
|
23
|
+
#
|
17
24
|
# # good
|
18
25
|
# response.parsed_body
|
19
26
|
class ResponseParsedBody < Base
|
20
27
|
extend AutoCorrector
|
21
28
|
extend TargetRailsVersion
|
22
29
|
|
23
|
-
MSG = 'Prefer `response.parsed_body` to `JSON.parse(response.body)`.'
|
24
|
-
|
25
30
|
RESTRICT_ON_SEND = %i[parse].freeze
|
26
31
|
|
27
32
|
minimum_target_rails_version 5.0
|
@@ -38,12 +43,27 @@ module RuboCop
|
|
38
43
|
)
|
39
44
|
PATTERN
|
40
45
|
|
46
|
+
# @!method nokogiri_html_parse_response_body(node)
|
47
|
+
def_node_matcher :nokogiri_html_parse_response_body, <<~PATTERN
|
48
|
+
(send
|
49
|
+
(const
|
50
|
+
(const {nil? cbase} :Nokogiri)
|
51
|
+
${:HTML :HTML5}
|
52
|
+
)
|
53
|
+
:parse
|
54
|
+
(send
|
55
|
+
(send nil? :response)
|
56
|
+
:body
|
57
|
+
)
|
58
|
+
)
|
59
|
+
PATTERN
|
60
|
+
|
41
61
|
def on_send(node)
|
42
|
-
|
62
|
+
check_json_parse_response_body(node)
|
43
63
|
|
44
|
-
|
45
|
-
|
46
|
-
|
64
|
+
return unless target_rails_version >= 7.1
|
65
|
+
|
66
|
+
check_nokogiri_html_parse_response_body(node)
|
47
67
|
end
|
48
68
|
|
49
69
|
private
|
@@ -51,6 +71,28 @@ module RuboCop
|
|
51
71
|
def autocorrect(corrector, node)
|
52
72
|
corrector.replace(node, 'response.parsed_body')
|
53
73
|
end
|
74
|
+
|
75
|
+
def check_json_parse_response_body(node)
|
76
|
+
return unless json_parse_response_body?(node)
|
77
|
+
|
78
|
+
add_offense(
|
79
|
+
node,
|
80
|
+
message: 'Prefer `response.parsed_body` to `JSON.parse(response.body)`.'
|
81
|
+
) do |corrector|
|
82
|
+
autocorrect(corrector, node)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def check_nokogiri_html_parse_response_body(node)
|
87
|
+
return unless (const = nokogiri_html_parse_response_body(node))
|
88
|
+
|
89
|
+
add_offense(
|
90
|
+
node,
|
91
|
+
message: "Prefer `response.parsed_body` to `Nokogiri::#{const}.parse(response.body)`."
|
92
|
+
) do |corrector|
|
93
|
+
autocorrect(corrector, node)
|
94
|
+
end
|
95
|
+
end
|
54
96
|
end
|
55
97
|
end
|
56
98
|
end
|
@@ -17,7 +17,7 @@ module RuboCop
|
|
17
17
|
# # good
|
18
18
|
# def change
|
19
19
|
# change_table :users do |t|
|
20
|
-
# t.remove :name, :string
|
20
|
+
# t.remove :name, type: :string
|
21
21
|
# end
|
22
22
|
# end
|
23
23
|
#
|
@@ -290,10 +290,10 @@ module RuboCop
|
|
290
290
|
when :change
|
291
291
|
false
|
292
292
|
when :remove
|
293
|
-
target_rails_version >= 6.1 && all_hash_key?(node.
|
293
|
+
target_rails_version >= 6.1 && all_hash_key?(node.last_argument, :type)
|
294
294
|
when :change_default, :change_column_default, :change_table_comment,
|
295
295
|
:change_column_comment
|
296
|
-
all_hash_key?(node.
|
296
|
+
all_hash_key?(node.last_argument, :from, :to)
|
297
297
|
else
|
298
298
|
true
|
299
299
|
end
|
@@ -307,7 +307,7 @@ module RuboCop
|
|
307
307
|
|
308
308
|
def within_reversible_or_up_only_block?(node)
|
309
309
|
node.each_ancestor(:block).any? do |ancestor|
|
310
|
-
(ancestor.block_type? && ancestor.
|
310
|
+
(ancestor.block_type? && ancestor.method?(:reversible)) || ancestor.method?(:up_only)
|
311
311
|
end
|
312
312
|
end
|
313
313
|
|
@@ -12,7 +12,7 @@ module RuboCop
|
|
12
12
|
# `Style/FileRead`, `Style/FileWrite` and `Rails/RootJoinChain`.
|
13
13
|
#
|
14
14
|
# @safety
|
15
|
-
# This cop is unsafe for autocorrection because
|
15
|
+
# This cop is unsafe for autocorrection because ``Dir``'s `children`, `each_child`, `entries`, and `glob`
|
16
16
|
# methods return string element, but these methods of `Pathname` return `Pathname` element.
|
17
17
|
#
|
18
18
|
# @example
|
@@ -32,13 +32,28 @@ module RuboCop
|
|
32
32
|
# Rails.root.join('db', 'schema.rb').write(content)
|
33
33
|
# Rails.root.join('db', 'schema.rb').binwrite(content)
|
34
34
|
#
|
35
|
-
class RootPathnameMethods < Base
|
35
|
+
class RootPathnameMethods < Base # rubocop:disable Metrics/ClassLength
|
36
36
|
extend AutoCorrector
|
37
37
|
include RangeHelp
|
38
38
|
|
39
39
|
MSG = '`%<rails_root>s` is a `Pathname` so you can just append `#%<method>s`.'
|
40
40
|
|
41
|
-
|
41
|
+
DIR_GLOB_METHODS = %i[glob].to_set.freeze
|
42
|
+
|
43
|
+
DIR_NON_GLOB_METHODS = %i[
|
44
|
+
children
|
45
|
+
delete
|
46
|
+
each_child
|
47
|
+
empty?
|
48
|
+
entries
|
49
|
+
exist?
|
50
|
+
mkdir
|
51
|
+
open
|
52
|
+
rmdir
|
53
|
+
unlink
|
54
|
+
].to_set.freeze
|
55
|
+
|
56
|
+
DIR_METHODS = (DIR_GLOB_METHODS + DIR_NON_GLOB_METHODS).freeze
|
42
57
|
|
43
58
|
FILE_METHODS = %i[
|
44
59
|
atime
|
@@ -134,7 +149,8 @@ module RuboCop
|
|
134
149
|
|
135
150
|
RESTRICT_ON_SEND = (DIR_METHODS + FILE_METHODS + FILE_TEST_METHODS + FILE_UTILS_METHODS).to_set.freeze
|
136
151
|
|
137
|
-
|
152
|
+
# @!method pathname_method_for_ruby_2_5_or_higher(node)
|
153
|
+
def_node_matcher :pathname_method_for_ruby_2_5_or_higher, <<~PATTERN
|
138
154
|
{
|
139
155
|
(send (const {nil? cbase} :Dir) $DIR_METHODS $_ $...)
|
140
156
|
(send (const {nil? cbase} {:IO :File}) $FILE_METHODS $_ $...)
|
@@ -143,6 +159,16 @@ module RuboCop
|
|
143
159
|
}
|
144
160
|
PATTERN
|
145
161
|
|
162
|
+
# @!method pathname_method_for_ruby_2_4_or_lower(node)
|
163
|
+
def_node_matcher :pathname_method_for_ruby_2_4_or_lower, <<~PATTERN
|
164
|
+
{
|
165
|
+
(send (const {nil? cbase} :Dir) $DIR_NON_GLOB_METHODS $_ $...)
|
166
|
+
(send (const {nil? cbase} {:IO :File}) $FILE_METHODS $_ $...)
|
167
|
+
(send (const {nil? cbase} :FileTest) $FILE_TEST_METHODS $_ $...)
|
168
|
+
(send (const {nil? cbase} :FileUtils) $FILE_UTILS_METHODS $_ $...)
|
169
|
+
}
|
170
|
+
PATTERN
|
171
|
+
|
146
172
|
def_node_matcher :dir_glob?, <<~PATTERN
|
147
173
|
(send
|
148
174
|
(const {cbase nil?} :Dir) :glob ...)
|
@@ -183,6 +209,14 @@ module RuboCop
|
|
183
209
|
yield(method, path, args, rails_root)
|
184
210
|
end
|
185
211
|
|
212
|
+
def pathname_method(node)
|
213
|
+
if target_ruby_version >= 2.5
|
214
|
+
pathname_method_for_ruby_2_5_or_higher(node)
|
215
|
+
else
|
216
|
+
pathname_method_for_ruby_2_4_or_lower(node)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
186
220
|
def build_path_glob_replacement(path, method)
|
187
221
|
receiver = range_between(path.source_range.begin_pos, path.children.first.loc.selector.end_pos).source
|
188
222
|
|
@@ -188,7 +188,7 @@ module RuboCop
|
|
188
188
|
end
|
189
189
|
|
190
190
|
def persisted_referenced?(assignment)
|
191
|
-
return unless assignment.referenced?
|
191
|
+
return false unless assignment.referenced?
|
192
192
|
|
193
193
|
assignment.variable.references.any? do |reference|
|
194
194
|
call_to_persisted?(reference.node.parent)
|
@@ -196,6 +196,8 @@ module RuboCop
|
|
196
196
|
end
|
197
197
|
|
198
198
|
def call_to_persisted?(node)
|
199
|
+
node = node.parent.condition if node.parenthesized_call? && node.parent.if_type?
|
200
|
+
|
199
201
|
node.send_type? && node.method?(:persisted?)
|
200
202
|
end
|
201
203
|
|
@@ -235,10 +237,10 @@ module RuboCop
|
|
235
237
|
|
236
238
|
def in_condition_or_compound_boolean?(node)
|
237
239
|
node = node.block_node || node
|
238
|
-
parent = node.
|
240
|
+
parent = node.each_ancestor.find { |ancestor| !ancestor.begin_type? }
|
239
241
|
return false unless parent
|
240
242
|
|
241
|
-
operator_or_single_negative?(parent) || (conditional?(parent) && node == parent.condition)
|
243
|
+
operator_or_single_negative?(parent) || (conditional?(parent) && node == deparenthesize(parent.condition))
|
242
244
|
end
|
243
245
|
|
244
246
|
def operator_or_single_negative?(node)
|
@@ -249,6 +251,11 @@ module RuboCop
|
|
249
251
|
parent.if_type? || parent.case_type?
|
250
252
|
end
|
251
253
|
|
254
|
+
def deparenthesize(node)
|
255
|
+
node = node.children.last while node.begin_type?
|
256
|
+
node
|
257
|
+
end
|
258
|
+
|
252
259
|
def checked_immediately?(node)
|
253
260
|
node.parent && call_to_persisted?(node.parent)
|
254
261
|
end
|
@@ -298,7 +305,7 @@ module RuboCop
|
|
298
305
|
|
299
306
|
node = assignable_node(node)
|
300
307
|
method, sibling_index = find_method_with_sibling_index(node.parent)
|
301
|
-
return unless method && (method.def_type? || method.block_type?)
|
308
|
+
return false unless method && (method.def_type? || method.block_type?)
|
302
309
|
|
303
310
|
method.children.size == node.sibling_index + sibling_index
|
304
311
|
end
|
@@ -331,10 +338,10 @@ module RuboCop
|
|
331
338
|
|
332
339
|
# Check argument signature as no arguments or one hash
|
333
340
|
def expected_signature?(node)
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
341
|
+
return true unless node.arguments?
|
342
|
+
return false if !node.arguments.one? || node.method?(:destroy)
|
343
|
+
|
344
|
+
node.first_argument.hash_type? || !node.first_argument.literal?
|
338
345
|
end
|
339
346
|
end
|
340
347
|
end
|
@@ -74,17 +74,25 @@ module RuboCop
|
|
74
74
|
def on_send(node)
|
75
75
|
if add_column_without_comment?(node)
|
76
76
|
add_offense(node, message: COLUMN_MSG)
|
77
|
-
elsif
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
add_offense(node.parent.body, message: COLUMN_MSG)
|
82
|
-
end
|
77
|
+
elsif create_table_without_comment?(node)
|
78
|
+
add_offense(node, message: TABLE_MSG)
|
79
|
+
elsif create_table_with_block?(node.parent)
|
80
|
+
check_column_within_create_table_block(node.parent.body)
|
83
81
|
end
|
84
82
|
end
|
85
83
|
|
86
84
|
private
|
87
85
|
|
86
|
+
def check_column_within_create_table_block(node)
|
87
|
+
if node.begin_type?
|
88
|
+
node.child_nodes.each do |child_node|
|
89
|
+
add_offense(child_node, message: COLUMN_MSG) if t_column_without_comment?(child_node)
|
90
|
+
end
|
91
|
+
elsif t_column_without_comment?(node)
|
92
|
+
add_offense(node, message: COLUMN_MSG)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
88
96
|
def add_column_without_comment?(node)
|
89
97
|
add_column?(node) && !add_column_with_comment?(node)
|
90
98
|
end
|
@@ -93,10 +101,8 @@ module RuboCop
|
|
93
101
|
create_table?(node) && !create_table_with_comment?(node)
|
94
102
|
end
|
95
103
|
|
96
|
-
def
|
97
|
-
|
98
|
-
t_column?(node.parent.body) &&
|
99
|
-
!t_column_with_comment?(node.parent.body)
|
104
|
+
def t_column_without_comment?(node)
|
105
|
+
t_column?(node) && !t_column_with_comment?(node)
|
100
106
|
end
|
101
107
|
end
|
102
108
|
end
|