rubocop-rails 2.19.1 → 2.30.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +70 -16
- data/config/default.yml +173 -28
- data/lib/rubocop/cop/mixin/active_record_helper.rb +16 -4
- data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +2 -2
- data/lib/rubocop/cop/mixin/database_type_resolvable.rb +66 -0
- data/lib/rubocop/cop/mixin/index_method.rb +68 -61
- data/lib/rubocop/cop/mixin/routes_helper.rb +20 -0
- data/lib/rubocop/cop/mixin/target_rails_version.rb +27 -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/action_order.rb +1 -5
- data/lib/rubocop/cop/rails/active_record_aliases.rb +2 -2
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +1 -5
- 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/add_column_index.rb +1 -0
- data/lib/rubocop/cop/rails/after_commit_override.rb +1 -1
- data/lib/rubocop/cop/rails/application_record.rb +4 -0
- data/lib/rubocop/cop/rails/assert_not.rb +0 -1
- data/lib/rubocop/cop/rails/belongs_to.rb +1 -1
- data/lib/rubocop/cop/rails/blank.rb +1 -1
- data/lib/rubocop/cop/rails/bulk_change_table.rb +19 -45
- data/lib/rubocop/cop/rails/compact_blank.rb +29 -8
- data/lib/rubocop/cop/rails/content_tag.rb +2 -2
- data/lib/rubocop/cop/rails/dangerous_column_names.rb +448 -0
- data/lib/rubocop/cop/rails/date.rb +14 -5
- data/lib/rubocop/cop/rails/delegate.rb +53 -7
- data/lib/rubocop/cop/rails/duplicate_association.rb +71 -10
- 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/enum_hash.rb +31 -8
- data/lib/rubocop/cop/rails/enum_syntax.rb +130 -0
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +29 -7
- data/lib/rubocop/cop/rails/env_local.rb +69 -0
- data/lib/rubocop/cop/rails/expanded_date_range.rb +1 -1
- data/lib/rubocop/cop/rails/file_path.rb +186 -18
- 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/find_each.rb +1 -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_positional_arguments.rb +7 -0
- 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/i18n_locale_texts.rb +5 -1
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +23 -3
- data/lib/rubocop/cop/rails/index_by.rb +28 -12
- data/lib/rubocop/cop/rails/index_with.rb +28 -12
- data/lib/rubocop/cop/rails/inquiry.rb +2 -1
- data/lib/rubocop/cop/rails/inverse_of.rb +1 -1
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +19 -10
- data/lib/rubocop/cop/rails/link_to_blank.rb +2 -2
- data/lib/rubocop/cop/rails/match_route.rb +1 -9
- data/lib/rubocop/cop/rails/multiple_route_paths.rb +50 -0
- data/lib/rubocop/cop/rails/not_null_column.rb +100 -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 +21 -1
- data/lib/rubocop/cop/rails/pluck_id.rb +2 -1
- data/lib/rubocop/cop/rails/pluck_in_where.rb +35 -13
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +30 -16
- data/lib/rubocop/cop/rails/presence.rb +1 -1
- data/lib/rubocop/cop/rails/present.rb +1 -3
- data/lib/rubocop/cop/rails/rake_environment.rb +22 -6
- data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +190 -0
- data/lib/rubocop/cop/rails/redundant_foreign_key.rb +1 -1
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +16 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +2 -2
- data/lib/rubocop/cop/rails/reflection_class_name.rb +2 -2
- data/lib/rubocop/cop/rails/refute_methods.rb +0 -1
- data/lib/rubocop/cop/rails/relative_date_constant.rb +1 -1
- data/lib/rubocop/cop/rails/render_plain_text.rb +6 -3
- data/lib/rubocop/cop/rails/request_referer.rb +1 -1
- data/lib/rubocop/cop/rails/response_parsed_body.rb +52 -10
- data/lib/rubocop/cop/rails/reversible_migration.rb +7 -5
- data/lib/rubocop/cop/rails/root_pathname_methods.rb +58 -15
- data/lib/rubocop/cop/rails/save_bang.rb +22 -14
- data/lib/rubocop/cop/rails/schema_comment.rb +17 -10
- data/lib/rubocop/cop/rails/select_map.rb +79 -0
- data/lib/rubocop/cop/rails/skips_model_validations.rb +9 -4
- data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +1 -2
- data/lib/rubocop/cop/rails/strip_heredoc.rb +1 -1
- data/lib/rubocop/cop/rails/strong_parameters_expect.rb +104 -0
- data/lib/rubocop/cop/rails/three_state_boolean_column.rb +4 -5
- data/lib/rubocop/cop/rails/time_zone.rb +26 -11
- data/lib/rubocop/cop/rails/transaction_exit_statement.rb +40 -9
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +11 -26
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +17 -21
- 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 +9 -4
- data/lib/rubocop/cop/rails/where_equals.rb +29 -12
- 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 +18 -11
- data/lib/rubocop/cop/rails/where_range.rb +203 -0
- data/lib/rubocop/cop/rails_cops.rb +11 -0
- data/lib/rubocop/rails/migration_file_skippable.rb +54 -0
- data/lib/rubocop/rails/plugin.rb +48 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +8 -7
- data/lib/rubocop/rails/schema_loader.rb +5 -15
- data/lib/rubocop/rails/version.rb +1 -1
- data/lib/rubocop/rails.rb +1 -8
- data/lib/rubocop-rails.rb +12 -4
- metadata +55 -11
- data/lib/rubocop/rails/inject.rb +0 -18
@@ -7,25 +7,37 @@ module RuboCop
|
|
7
7
|
# and can be replaced with `select`.
|
8
8
|
#
|
9
9
|
# Since `pluck` is an eager method and hits the database immediately,
|
10
|
-
# using `select` helps to avoid additional database queries
|
10
|
+
# using `select` helps to avoid additional database queries by running as
|
11
|
+
# a subquery.
|
11
12
|
#
|
12
|
-
# This cop has two
|
13
|
-
#
|
14
|
-
# (
|
13
|
+
# This cop has two modes of enforcement. When the `EnforcedStyle` is set
|
14
|
+
# to `conservative` (the default), only calls to `pluck` on a constant
|
15
|
+
# (e.g. a model class) within `where` are considered offenses.
|
15
16
|
#
|
16
17
|
# @safety
|
17
|
-
# When
|
18
|
-
# `where`
|
19
|
-
#
|
20
|
-
# `ActiveRecord::Relation` instance
|
18
|
+
# When `EnforcedStyle` is set to `aggressive`, all calls to `pluck`
|
19
|
+
# within `where` are considered offenses. This might lead to false
|
20
|
+
# positives because the check cannot distinguish between calls to
|
21
|
+
# `pluck` on an `ActiveRecord::Relation` instance and calls to `pluck`
|
22
|
+
# on an `Array` instance.
|
23
|
+
#
|
24
|
+
# Additionally, when using a subquery with the SQL `IN` operator,
|
25
|
+
# databases like PostgreSQL and MySQL can't optimize complex queries as
|
26
|
+
# well. They need to scan all records of the outer table against the
|
27
|
+
# subquery result sequentially, rather than using an index. This can
|
28
|
+
# cause significant performance issues compared to writing the query
|
29
|
+
# differently or using `pluck`.
|
21
30
|
#
|
22
31
|
# @example
|
23
32
|
# # bad
|
24
33
|
# Post.where(user_id: User.active.pluck(:id))
|
34
|
+
# Post.where(user_id: User.active.ids)
|
35
|
+
# Post.where.not(user_id: User.active.pluck(:id))
|
25
36
|
#
|
26
37
|
# # good
|
27
38
|
# Post.where(user_id: User.active.select(:id))
|
28
39
|
# Post.where(user_id: active_users.select(:id))
|
40
|
+
# Post.where.not(user_id: active_users.select(:id))
|
29
41
|
#
|
30
42
|
# @example EnforcedStyle: conservative (default)
|
31
43
|
# # good
|
@@ -40,8 +52,9 @@ module RuboCop
|
|
40
52
|
include ConfigurableEnforcedStyle
|
41
53
|
extend AutoCorrector
|
42
54
|
|
43
|
-
|
44
|
-
|
55
|
+
MSG_SELECT = 'Use `select` instead of `pluck` within `where` query method.'
|
56
|
+
MSG_IDS = 'Use `select(:id)` instead of `ids` within `where` query method.'
|
57
|
+
RESTRICT_ON_SEND = %i[pluck ids].freeze
|
45
58
|
|
46
59
|
def on_send(node)
|
47
60
|
return unless in_where?(node)
|
@@ -49,17 +62,26 @@ module RuboCop
|
|
49
62
|
|
50
63
|
range = node.loc.selector
|
51
64
|
|
52
|
-
|
53
|
-
|
65
|
+
if node.method?(:ids)
|
66
|
+
replacement = 'select(:id)'
|
67
|
+
message = MSG_IDS
|
68
|
+
else
|
69
|
+
replacement = 'select'
|
70
|
+
message = MSG_SELECT
|
71
|
+
end
|
72
|
+
|
73
|
+
add_offense(range, message: message) do |corrector|
|
74
|
+
corrector.replace(range, replacement)
|
54
75
|
end
|
55
76
|
end
|
77
|
+
alias on_csend on_send
|
56
78
|
|
57
79
|
private
|
58
80
|
|
59
81
|
def root_receiver(node)
|
60
82
|
receiver = node.receiver
|
61
83
|
|
62
|
-
if receiver&.
|
84
|
+
if receiver&.call_type?
|
63
85
|
root_receiver(receiver)
|
64
86
|
else
|
65
87
|
receiver
|
@@ -10,25 +10,39 @@ module RuboCop
|
|
10
10
|
# # bad
|
11
11
|
# 3.day.ago
|
12
12
|
# 1.months.ago
|
13
|
+
# 5.megabyte
|
14
|
+
# 1.gigabytes
|
13
15
|
#
|
14
16
|
# # good
|
15
17
|
# 3.days.ago
|
16
18
|
# 1.month.ago
|
19
|
+
# 5.megabytes
|
20
|
+
# 1.gigabyte
|
17
21
|
class PluralizationGrammar < Base
|
18
22
|
extend AutoCorrector
|
19
23
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
24
|
+
SINGULAR_METHODS = {
|
25
|
+
second: :seconds,
|
26
|
+
minute: :minutes,
|
27
|
+
hour: :hours,
|
28
|
+
day: :days,
|
29
|
+
week: :weeks,
|
30
|
+
fortnight: :fortnights,
|
31
|
+
month: :months,
|
32
|
+
year: :years,
|
33
|
+
byte: :bytes,
|
34
|
+
kilobyte: :kilobytes,
|
35
|
+
megabyte: :megabytes,
|
36
|
+
gigabyte: :gigabytes,
|
37
|
+
terabyte: :terabytes,
|
38
|
+
petabyte: :petabytes,
|
39
|
+
exabyte: :exabytes,
|
40
|
+
zettabyte: :zettabytes
|
41
|
+
}.freeze
|
42
|
+
|
43
|
+
RESTRICT_ON_SEND = SINGULAR_METHODS.keys + SINGULAR_METHODS.values
|
44
|
+
|
45
|
+
PLURAL_METHODS = SINGULAR_METHODS.invert.freeze
|
32
46
|
|
33
47
|
MSG = 'Prefer `%<number>s.%<correct>s`.'
|
34
48
|
|
@@ -82,19 +96,19 @@ module RuboCop
|
|
82
96
|
end
|
83
97
|
|
84
98
|
def literal_number?(node)
|
85
|
-
node
|
99
|
+
node&.type?(:int, :float)
|
86
100
|
end
|
87
101
|
|
88
102
|
def pluralize(method_name)
|
89
|
-
|
103
|
+
SINGULAR_METHODS.fetch(method_name.to_sym).to_s
|
90
104
|
end
|
91
105
|
|
92
106
|
def singularize(method_name)
|
93
|
-
|
107
|
+
PLURAL_METHODS.fetch(method_name.to_sym).to_s
|
94
108
|
end
|
95
109
|
|
96
110
|
def duration_method?(method_name)
|
97
|
-
|
111
|
+
SINGULAR_METHODS.key?(method_name) || PLURAL_METHODS.key?(method_name)
|
98
112
|
end
|
99
113
|
end
|
100
114
|
end
|
@@ -98,8 +98,6 @@ module RuboCop
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def on_or(node)
|
101
|
-
return unless cop_config['NilOrEmpty']
|
102
|
-
|
103
101
|
exists_and_not_empty?(node) do |var1, var2|
|
104
102
|
return unless var1 == var2
|
105
103
|
|
@@ -112,7 +110,7 @@ module RuboCop
|
|
112
110
|
def on_if(node)
|
113
111
|
return unless cop_config['UnlessBlank']
|
114
112
|
return unless node.unless?
|
115
|
-
return if node.else? && config.
|
113
|
+
return if node.else? && config.cop_enabled?('Style/UnlessElse')
|
116
114
|
|
117
115
|
unless_blank?(node) do |method_call, receiver|
|
118
116
|
range = unless_condition(node, method_call)
|
@@ -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,190 @@
|
|
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
|
+
# For the methods `delete_all` and `destroy_all`, this cop will only check cases where the receiver is a model.
|
9
|
+
# It will ignore cases where the receiver is an association (e.g., `user.articles.all.delete_all`).
|
10
|
+
# This is because omitting `all` from an association changes the methods
|
11
|
+
# from `ActiveRecord::Relation` to `ActiveRecord::Associations::CollectionProxy`,
|
12
|
+
# which can affect their behavior.
|
13
|
+
#
|
14
|
+
# @safety
|
15
|
+
# This cop is unsafe for autocorrection if the receiver for `all` is not an Active Record object.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# # bad
|
19
|
+
# User.all.find(id)
|
20
|
+
# User.all.order(:created_at)
|
21
|
+
# users.all.where(id: ids)
|
22
|
+
# user.articles.all.order(:created_at)
|
23
|
+
#
|
24
|
+
# # good
|
25
|
+
# User.find(id)
|
26
|
+
# User.order(:created_at)
|
27
|
+
# users.where(id: ids)
|
28
|
+
# user.articles.order(:created_at)
|
29
|
+
#
|
30
|
+
# @example AllowedReceivers: ['ActionMailer::Preview', 'ActiveSupport::TimeZone'] (default)
|
31
|
+
# # good
|
32
|
+
# ActionMailer::Preview.all.first
|
33
|
+
# ActiveSupport::TimeZone.all.first
|
34
|
+
class RedundantActiveRecordAllMethod < Base
|
35
|
+
include ActiveRecordHelper
|
36
|
+
include AllowedReceivers
|
37
|
+
include RangeHelp
|
38
|
+
extend AutoCorrector
|
39
|
+
|
40
|
+
MSG = 'Redundant `all` detected.'
|
41
|
+
|
42
|
+
RESTRICT_ON_SEND = [:all].freeze
|
43
|
+
|
44
|
+
# Defined methods in `ActiveRecord::Querying::QUERYING_METHODS` on activerecord 7.1.0.
|
45
|
+
QUERYING_METHODS = %i[
|
46
|
+
and
|
47
|
+
annotate
|
48
|
+
any?
|
49
|
+
async_average
|
50
|
+
async_count
|
51
|
+
async_ids
|
52
|
+
async_maximum
|
53
|
+
async_minimum
|
54
|
+
async_pick
|
55
|
+
async_pluck
|
56
|
+
async_sum
|
57
|
+
average
|
58
|
+
calculate
|
59
|
+
count
|
60
|
+
create_or_find_by
|
61
|
+
create_or_find_by!
|
62
|
+
create_with
|
63
|
+
delete_all
|
64
|
+
delete_by
|
65
|
+
destroy_all
|
66
|
+
destroy_by
|
67
|
+
distinct
|
68
|
+
eager_load
|
69
|
+
except
|
70
|
+
excluding
|
71
|
+
exists?
|
72
|
+
extending
|
73
|
+
extract_associated
|
74
|
+
fifth
|
75
|
+
fifth!
|
76
|
+
find
|
77
|
+
find_by
|
78
|
+
find_by!
|
79
|
+
find_each
|
80
|
+
find_in_batches
|
81
|
+
find_or_create_by
|
82
|
+
find_or_create_by!
|
83
|
+
find_or_initialize_by
|
84
|
+
find_sole_by
|
85
|
+
first
|
86
|
+
first!
|
87
|
+
first_or_create
|
88
|
+
first_or_create!
|
89
|
+
first_or_initialize
|
90
|
+
forty_two
|
91
|
+
forty_two!
|
92
|
+
fourth
|
93
|
+
fourth!
|
94
|
+
from
|
95
|
+
group
|
96
|
+
having
|
97
|
+
ids
|
98
|
+
in_batches
|
99
|
+
in_order_of
|
100
|
+
includes
|
101
|
+
invert_where
|
102
|
+
joins
|
103
|
+
last
|
104
|
+
last!
|
105
|
+
left_joins
|
106
|
+
left_outer_joins
|
107
|
+
limit
|
108
|
+
lock
|
109
|
+
many?
|
110
|
+
maximum
|
111
|
+
merge
|
112
|
+
minimum
|
113
|
+
none
|
114
|
+
none?
|
115
|
+
offset
|
116
|
+
one?
|
117
|
+
only
|
118
|
+
optimizer_hints
|
119
|
+
or
|
120
|
+
order
|
121
|
+
pick
|
122
|
+
pluck
|
123
|
+
preload
|
124
|
+
readonly
|
125
|
+
references
|
126
|
+
regroup
|
127
|
+
reorder
|
128
|
+
reselect
|
129
|
+
rewhere
|
130
|
+
second
|
131
|
+
second!
|
132
|
+
second_to_last
|
133
|
+
second_to_last!
|
134
|
+
select
|
135
|
+
sole
|
136
|
+
strict_loading
|
137
|
+
sum
|
138
|
+
take
|
139
|
+
take!
|
140
|
+
third
|
141
|
+
third!
|
142
|
+
third_to_last
|
143
|
+
third_to_last!
|
144
|
+
touch_all
|
145
|
+
unscope
|
146
|
+
update_all
|
147
|
+
where
|
148
|
+
with
|
149
|
+
without
|
150
|
+
].to_set.freeze
|
151
|
+
|
152
|
+
POSSIBLE_ENUMERABLE_BLOCK_METHODS = %i[any? count find none? one? select sum].freeze
|
153
|
+
SENSITIVE_METHODS_ON_ASSOCIATION = %i[delete_all destroy_all].freeze
|
154
|
+
|
155
|
+
def_node_matcher :followed_by_query_method?, <<~PATTERN
|
156
|
+
(send (send _ :all) QUERYING_METHODS ...)
|
157
|
+
PATTERN
|
158
|
+
|
159
|
+
def on_send(node)
|
160
|
+
return unless followed_by_query_method?(node.parent)
|
161
|
+
return if possible_enumerable_block_method?(node) || sensitive_association_method?(node)
|
162
|
+
return if node.receiver ? allowed_receiver?(node.receiver) : !inherit_active_record_base?(node)
|
163
|
+
|
164
|
+
range_of_all_method = offense_range(node)
|
165
|
+
add_offense(range_of_all_method) do |collector|
|
166
|
+
collector.remove(range_of_all_method)
|
167
|
+
collector.remove(node.parent.loc.dot)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
def possible_enumerable_block_method?(node)
|
174
|
+
parent = node.parent
|
175
|
+
return false unless POSSIBLE_ENUMERABLE_BLOCK_METHODS.include?(parent.method_name)
|
176
|
+
|
177
|
+
parent.block_literal? || parent.first_argument&.block_pass_type?
|
178
|
+
end
|
179
|
+
|
180
|
+
def sensitive_association_method?(node)
|
181
|
+
!node.receiver&.const_type? && SENSITIVE_METHODS_ON_ASSOCIATION.include?(node.parent.method_name)
|
182
|
+
end
|
183
|
+
|
184
|
+
def offense_range(node)
|
185
|
+
range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -40,7 +40,7 @@ module RuboCop
|
|
40
40
|
def on_send(node)
|
41
41
|
association_with_foreign_key(node) do |type, name, options, foreign_key_pair, foreign_key|
|
42
42
|
if redundant?(node, type, name, options, foreign_key)
|
43
|
-
add_offense(foreign_key_pair
|
43
|
+
add_offense(foreign_key_pair) do |corrector|
|
44
44
|
range = range_with_surrounding_space(foreign_key_pair.source_range, side: :left)
|
45
45
|
range = range_with_surrounding_comma(range, :left)
|
46
46
|
|
@@ -39,6 +39,9 @@ module RuboCop
|
|
39
39
|
MSG = 'Remove explicit presence validation for %<association>s.'
|
40
40
|
RESTRICT_ON_SEND = %i[validates].freeze
|
41
41
|
|
42
|
+
# From https://github.com/rails/rails/blob/7a0bf93b9dd291c7f61121a41b3a813ac8857e6a/activemodel/lib/active_model/validations/validates.rb#L157-L159
|
43
|
+
NON_VALIDATION_OPTIONS = %i[if unless on allow_blank allow_nil strict].freeze
|
44
|
+
|
42
45
|
minimum_target_rails_version 5.0
|
43
46
|
|
44
47
|
# @!method presence_validation?(node)
|
@@ -53,6 +56,12 @@ module RuboCop
|
|
53
56
|
# @example source that matches - by a foreign key
|
54
57
|
# validates :user_id, presence: true
|
55
58
|
#
|
59
|
+
# @example source that DOES NOT match - if condition
|
60
|
+
# validates :user_id, presence: true, if: condition
|
61
|
+
#
|
62
|
+
# @example source that DOES NOT match - unless condition
|
63
|
+
# validates :user_id, presence: true, unless: condition
|
64
|
+
#
|
56
65
|
# @example source that DOES NOT match - strict validation
|
57
66
|
# validates :user_id, presence: true, strict: true
|
58
67
|
#
|
@@ -65,6 +74,7 @@ module RuboCop
|
|
65
74
|
$[
|
66
75
|
(hash <$(pair (sym :presence) true) ...>) # presence: true
|
67
76
|
!(hash <$(pair (sym :strict) {true const}) ...>) # strict: true
|
77
|
+
!(hash <$(pair (sym {:if :unless}) _) ...>) # if: some_condition or unless: some_condition
|
68
78
|
]
|
69
79
|
)
|
70
80
|
PATTERN
|
@@ -163,6 +173,12 @@ module RuboCop
|
|
163
173
|
|
164
174
|
def on_send(node)
|
165
175
|
presence_validation?(node) do |all_keys, options, presence|
|
176
|
+
# If presence is the only validation option and other non-validation options
|
177
|
+
# are present, removing it will cause rails to error.
|
178
|
+
used_option_keys = options.keys.select(&:sym_type?).map(&:value)
|
179
|
+
remaining_validations = used_option_keys - NON_VALIDATION_OPTIONS - [:presence]
|
180
|
+
return if remaining_validations.none? && options.keys.length > 1
|
181
|
+
|
166
182
|
keys = non_optional_belongs_to(node.parent, all_keys)
|
167
183
|
return if keys.none?
|
168
184
|
|
@@ -78,7 +78,7 @@ module RuboCop
|
|
78
78
|
|
79
79
|
send_nodes.each do |send_node|
|
80
80
|
receiver = send_node.receiver
|
81
|
-
add_offense(receiver
|
81
|
+
add_offense(receiver) do |corrector|
|
82
82
|
autocorrect(corrector, send_node, node)
|
83
83
|
end
|
84
84
|
end
|
@@ -100,7 +100,7 @@ module RuboCop
|
|
100
100
|
else
|
101
101
|
return false if node.arguments.empty?
|
102
102
|
|
103
|
-
arg = node.
|
103
|
+
arg = node.first_argument
|
104
104
|
->(n) { same_value?(arg, n.receiver) }
|
105
105
|
end
|
106
106
|
|
@@ -40,10 +40,10 @@ module RuboCop
|
|
40
40
|
|
41
41
|
def on_send(node)
|
42
42
|
association_with_reflection(node) do |reflection_class_name|
|
43
|
-
return if reflection_class_name.value.send_type? && reflection_class_name.value.receiver
|
43
|
+
return if reflection_class_name.value.send_type? && !reflection_class_name.value.receiver&.const_type?
|
44
44
|
return if reflection_class_name.value.lvar_type? && str_assigned?(reflection_class_name)
|
45
45
|
|
46
|
-
add_offense(reflection_class_name
|
46
|
+
add_offense(reflection_class_name) do |corrector|
|
47
47
|
autocorrect(corrector, reflection_class_name)
|
48
48
|
end
|
49
49
|
end
|
@@ -53,9 +53,12 @@ module RuboCop
|
|
53
53
|
node.pairs.find { |p| p.key.value.to_sym == :content_type }
|
54
54
|
end
|
55
55
|
|
56
|
-
def compatible_content_type?(
|
57
|
-
|
58
|
-
|
56
|
+
def compatible_content_type?(pair_node)
|
57
|
+
if pair_node.nil?
|
58
|
+
!cop_config['ContentTypeCompatibility']
|
59
|
+
elsif pair_node.value.respond_to?(:value)
|
60
|
+
pair_node.value.value == 'text/plain'
|
61
|
+
end
|
59
62
|
end
|
60
63
|
|
61
64
|
def replacement(rest_options, option_value)
|
@@ -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
|