rubocop-rails 2.20.2 → 2.22.1
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 +6 -2
- data/config/default.yml +68 -8
- 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/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/action_filter.rb +3 -0
- data/lib/rubocop/cop/rails/bulk_change_table.rb +5 -38
- data/lib/rubocop/cop/rails/dangerous_column_names.rb +447 -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/env_local.rb +46 -0
- data/lib/rubocop/cop/rails/file_path.rb +4 -1
- 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 +4 -3
- data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +63 -13
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +7 -8
- data/lib/rubocop/cop/rails/not_null_column.rb +13 -3
- data/lib/rubocop/cop/rails/output.rb +3 -2
- data/lib/rubocop/cop/rails/rake_environment.rb +20 -4
- data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +207 -0
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +7 -0
- 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 +9 -4
- 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/time_zone.rb +12 -5
- data/lib/rubocop/cop/rails/transaction_exit_statement.rb +29 -10
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +1 -1
- data/lib/rubocop/cop/rails/unknown_env.rb +5 -1
- 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 +6 -0
- 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 +9 -3
@@ -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
|
@@ -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,8 +3,13 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
# Checks for add_column call with NOT NULL constraint
|
7
|
-
#
|
6
|
+
# Checks for add_column call with NOT NULL constraint in migration file.
|
7
|
+
#
|
8
|
+
# `TEXT` can have default values in PostgreSQL, but not in MySQL.
|
9
|
+
# It will automatically detect an adapter from `development` environment
|
10
|
+
# in `config/database.yml` or the environment variable `DATABASE_URL`
|
11
|
+
# when the `Database` option is not set. If the database is MySQL,
|
12
|
+
# this cop ignores offenses for the `TEXT`.
|
8
13
|
#
|
9
14
|
# @example
|
10
15
|
# # bad
|
@@ -17,6 +22,8 @@ module RuboCop
|
|
17
22
|
# add_reference :products, :category
|
18
23
|
# add_reference :products, :category, null: false, default: 1
|
19
24
|
class NotNullColumn < Base
|
25
|
+
include DatabaseTypeResolvable
|
26
|
+
|
20
27
|
MSG = 'Do not add a NOT NULL column without a default value.'
|
21
28
|
RESTRICT_ON_SEND = %i[add_column add_reference].freeze
|
22
29
|
|
@@ -45,7 +52,10 @@ module RuboCop
|
|
45
52
|
|
46
53
|
def check_add_column(node)
|
47
54
|
add_not_null_column?(node) do |type, pairs|
|
48
|
-
|
55
|
+
if type.respond_to?(:value)
|
56
|
+
return if type.value == :virtual || type.value == 'virtual'
|
57
|
+
return if (type.value == :text || type.value == 'text') && database == MYSQL
|
58
|
+
end
|
49
59
|
|
50
60
|
check_pairs(pairs)
|
51
61
|
end
|
@@ -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
|
|
@@ -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,207 @@
|
|
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.1.0.
|
39
|
+
QUERYING_METHODS = %i[
|
40
|
+
and
|
41
|
+
annotate
|
42
|
+
any?
|
43
|
+
async_average
|
44
|
+
async_count
|
45
|
+
async_ids
|
46
|
+
async_maximum
|
47
|
+
async_minimum
|
48
|
+
async_pick
|
49
|
+
async_pluck
|
50
|
+
async_sum
|
51
|
+
average
|
52
|
+
calculate
|
53
|
+
count
|
54
|
+
create_or_find_by
|
55
|
+
create_or_find_by!
|
56
|
+
create_with
|
57
|
+
delete_all
|
58
|
+
delete_by
|
59
|
+
destroy_all
|
60
|
+
destroy_by
|
61
|
+
distinct
|
62
|
+
eager_load
|
63
|
+
except
|
64
|
+
excluding
|
65
|
+
exists?
|
66
|
+
extending
|
67
|
+
extract_associated
|
68
|
+
fifth
|
69
|
+
fifth!
|
70
|
+
find
|
71
|
+
find_by
|
72
|
+
find_by!
|
73
|
+
find_each
|
74
|
+
find_in_batches
|
75
|
+
find_or_create_by
|
76
|
+
find_or_create_by!
|
77
|
+
find_or_initialize_by
|
78
|
+
find_sole_by
|
79
|
+
first
|
80
|
+
first!
|
81
|
+
first_or_create
|
82
|
+
first_or_create!
|
83
|
+
first_or_initialize
|
84
|
+
forty_two
|
85
|
+
forty_two!
|
86
|
+
fourth
|
87
|
+
fourth!
|
88
|
+
from
|
89
|
+
group
|
90
|
+
having
|
91
|
+
ids
|
92
|
+
in_batches
|
93
|
+
in_order_of
|
94
|
+
includes
|
95
|
+
invert_where
|
96
|
+
joins
|
97
|
+
last
|
98
|
+
last!
|
99
|
+
left_joins
|
100
|
+
left_outer_joins
|
101
|
+
limit
|
102
|
+
lock
|
103
|
+
many?
|
104
|
+
maximum
|
105
|
+
merge
|
106
|
+
minimum
|
107
|
+
none
|
108
|
+
none?
|
109
|
+
offset
|
110
|
+
one?
|
111
|
+
only
|
112
|
+
optimizer_hints
|
113
|
+
or
|
114
|
+
order
|
115
|
+
pick
|
116
|
+
pluck
|
117
|
+
preload
|
118
|
+
readonly
|
119
|
+
references
|
120
|
+
regroup
|
121
|
+
reorder
|
122
|
+
reselect
|
123
|
+
rewhere
|
124
|
+
second
|
125
|
+
second!
|
126
|
+
second_to_last
|
127
|
+
second_to_last!
|
128
|
+
select
|
129
|
+
sole
|
130
|
+
strict_loading
|
131
|
+
sum
|
132
|
+
take
|
133
|
+
take!
|
134
|
+
third
|
135
|
+
third!
|
136
|
+
third_to_last
|
137
|
+
third_to_last!
|
138
|
+
touch_all
|
139
|
+
unscope
|
140
|
+
update_all
|
141
|
+
where
|
142
|
+
with
|
143
|
+
without
|
144
|
+
].to_set.freeze
|
145
|
+
|
146
|
+
POSSIBLE_ENUMERABLE_BLOCK_METHODS = %i[any? count find none? one? select sum].freeze
|
147
|
+
|
148
|
+
def_node_matcher :followed_by_query_method?, <<~PATTERN
|
149
|
+
(send (send _ :all) QUERYING_METHODS ...)
|
150
|
+
PATTERN
|
151
|
+
|
152
|
+
def on_send(node)
|
153
|
+
return if !followed_by_query_method?(node.parent) || possible_enumerable_block_method?(node)
|
154
|
+
return if node.receiver ? allowed_receiver?(node.receiver) : !inherit_active_record_base?(node)
|
155
|
+
|
156
|
+
range_of_all_method = offense_range(node)
|
157
|
+
add_offense(range_of_all_method) do |collector|
|
158
|
+
collector.remove(range_of_all_method)
|
159
|
+
collector.remove(node.parent.loc.dot)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
def possible_enumerable_block_method?(node)
|
166
|
+
parent = node.parent
|
167
|
+
return false unless POSSIBLE_ENUMERABLE_BLOCK_METHODS.include?(parent.method_name)
|
168
|
+
|
169
|
+
parent.parent&.block_type? || parent.parent&.numblock_type? || parent.first_argument&.block_pass_type?
|
170
|
+
end
|
171
|
+
|
172
|
+
def offense_range(node)
|
173
|
+
range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
|
174
|
+
end
|
175
|
+
|
176
|
+
# TODO: In the future, please support only RuboCop 1.52+ and use `RuboCop::Cop::AllowedReceivers`:
|
177
|
+
# https://github.com/rubocop/rubocop/blob/v1.52.0/lib/rubocop/cop/mixin/allowed_receivers.rb
|
178
|
+
# At that time, this duplicated module implementation can be removed.
|
179
|
+
module AllowedReceivers
|
180
|
+
def allowed_receiver?(receiver)
|
181
|
+
receiver_name = receiver_name(receiver)
|
182
|
+
|
183
|
+
allowed_receivers.include?(receiver_name)
|
184
|
+
end
|
185
|
+
|
186
|
+
def receiver_name(receiver)
|
187
|
+
return receiver_name(receiver.receiver) if receiver.receiver && !receiver.receiver.const_type?
|
188
|
+
|
189
|
+
if receiver.send_type?
|
190
|
+
if receiver.receiver
|
191
|
+
"#{receiver_name(receiver.receiver)}.#{receiver.method_name}"
|
192
|
+
else
|
193
|
+
receiver.method_name.to_s
|
194
|
+
end
|
195
|
+
else
|
196
|
+
receiver.source
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def allowed_receivers
|
201
|
+
cop_config.fetch('AllowedReceivers', [])
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
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
|
@@ -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)
|
@@ -235,10 +235,10 @@ module RuboCop
|
|
235
235
|
|
236
236
|
def in_condition_or_compound_boolean?(node)
|
237
237
|
node = node.block_node || node
|
238
|
-
parent = node.
|
238
|
+
parent = node.each_ancestor.find { |ancestor| !ancestor.begin_type? }
|
239
239
|
return false unless parent
|
240
240
|
|
241
|
-
operator_or_single_negative?(parent) || (conditional?(parent) && node == parent.condition)
|
241
|
+
operator_or_single_negative?(parent) || (conditional?(parent) && node == deparenthesize(parent.condition))
|
242
242
|
end
|
243
243
|
|
244
244
|
def operator_or_single_negative?(node)
|
@@ -249,6 +249,11 @@ module RuboCop
|
|
249
249
|
parent.if_type? || parent.case_type?
|
250
250
|
end
|
251
251
|
|
252
|
+
def deparenthesize(node)
|
253
|
+
node = node.children.last while node.begin_type?
|
254
|
+
node
|
255
|
+
end
|
256
|
+
|
252
257
|
def checked_immediately?(node)
|
253
258
|
node.parent && call_to_persisted?(node.parent)
|
254
259
|
end
|
@@ -298,7 +303,7 @@ module RuboCop
|
|
298
303
|
|
299
304
|
node = assignable_node(node)
|
300
305
|
method, sibling_index = find_method_with_sibling_index(node.parent)
|
301
|
-
return unless method && (method.def_type? || method.block_type?)
|
306
|
+
return false unless method && (method.def_type? || method.block_type?)
|
302
307
|
|
303
308
|
method.children.size == node.sibling_index + sibling_index
|
304
309
|
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
|