rubocop-rails 2.8.0 → 2.10.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/LICENSE.txt +1 -1
- data/README.md +18 -2
- data/config/default.yml +101 -5
- data/config/obsoletion.yml +7 -0
- data/lib/rubocop/cop/mixin/active_record_helper.rb +16 -3
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +40 -0
- data/lib/rubocop/cop/mixin/index_method.rb +8 -11
- data/lib/rubocop/cop/rails/action_filter.rb +10 -14
- data/lib/rubocop/cop/rails/active_record_aliases.rb +13 -17
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +17 -12
- data/lib/rubocop/cop/rails/active_record_override.rb +1 -1
- data/lib/rubocop/cop/rails/active_support_aliases.rb +12 -21
- data/lib/rubocop/cop/rails/after_commit_override.rb +9 -2
- data/lib/rubocop/cop/rails/application_controller.rb +3 -7
- data/lib/rubocop/cop/rails/application_job.rb +2 -1
- data/lib/rubocop/cop/rails/application_mailer.rb +2 -7
- data/lib/rubocop/cop/rails/application_record.rb +2 -7
- data/lib/rubocop/cop/rails/arel_star.rb +41 -0
- data/lib/rubocop/cop/rails/assert_not.rb +8 -10
- data/lib/rubocop/cop/rails/attribute_default_block_value.rb +90 -0
- data/lib/rubocop/cop/rails/belongs_to.rb +10 -19
- data/lib/rubocop/cop/rails/blank.rb +31 -27
- data/lib/rubocop/cop/rails/bulk_change_table.rb +1 -1
- data/lib/rubocop/cop/rails/content_tag.rb +33 -18
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +2 -1
- data/lib/rubocop/cop/rails/date.rb +10 -11
- data/lib/rubocop/cop/rails/default_scope.rb +11 -4
- data/lib/rubocop/cop/rails/delegate.rb +9 -9
- data/lib/rubocop/cop/rails/delegate_allow_blank.rb +7 -8
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +15 -12
- data/lib/rubocop/cop/rails/enum_hash.rb +11 -10
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +2 -1
- data/lib/rubocop/cop/rails/environment_comparison.rb +18 -14
- data/lib/rubocop/cop/rails/environment_variable_access.rb +67 -0
- data/lib/rubocop/cop/rails/exit.rb +4 -10
- data/lib/rubocop/cop/rails/file_path.rb +6 -7
- data/lib/rubocop/cop/rails/find_by.rb +13 -13
- data/lib/rubocop/cop/rails/find_by_id.rb +12 -21
- data/lib/rubocop/cop/rails/find_each.rb +19 -18
- data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +3 -2
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +37 -6
- data/lib/rubocop/cop/rails/helper_instance_variable.rb +29 -3
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +32 -21
- data/lib/rubocop/cop/rails/http_status.rb +7 -9
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +8 -6
- data/lib/rubocop/cop/rails/index_by.rb +3 -2
- data/lib/rubocop/cop/rails/index_with.rb +3 -2
- data/lib/rubocop/cop/rails/inquiry.rb +4 -3
- data/lib/rubocop/cop/rails/inverse_of.rb +3 -2
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +17 -15
- data/lib/rubocop/cop/rails/link_to_blank.rb +25 -23
- data/lib/rubocop/cop/rails/mailer_name.rb +19 -13
- data/lib/rubocop/cop/rails/match_route.rb +14 -13
- data/lib/rubocop/cop/rails/negate_include.rb +10 -8
- data/lib/rubocop/cop/rails/not_null_column.rb +2 -1
- data/lib/rubocop/cop/rails/order_by_id.rb +1 -2
- data/lib/rubocop/cop/rails/output.rb +5 -2
- data/lib/rubocop/cop/rails/output_safety.rb +3 -2
- data/lib/rubocop/cop/rails/pick.rb +14 -12
- data/lib/rubocop/cop/rails/pluck.rb +6 -9
- data/lib/rubocop/cop/rails/pluck_id.rb +4 -6
- data/lib/rubocop/cop/rails/pluck_in_where.rb +7 -7
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +10 -14
- data/lib/rubocop/cop/rails/presence.rb +12 -13
- data/lib/rubocop/cop/rails/present.rb +30 -24
- data/lib/rubocop/cop/rails/rake_environment.rb +8 -10
- data/lib/rubocop/cop/rails/read_write_attribute.rb +12 -11
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +29 -31
- data/lib/rubocop/cop/rails/redundant_foreign_key.rb +9 -12
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +11 -10
- data/lib/rubocop/cop/rails/reflection_class_name.rb +17 -3
- data/lib/rubocop/cop/rails/refute_methods.rb +9 -10
- data/lib/rubocop/cop/rails/relative_date_constant.rb +30 -21
- data/lib/rubocop/cop/rails/render_inline.rb +2 -1
- data/lib/rubocop/cop/rails/render_plain_text.rb +9 -14
- data/lib/rubocop/cop/rails/request_referer.rb +7 -7
- data/lib/rubocop/cop/rails/require_dependency.rb +38 -0
- data/lib/rubocop/cop/rails/reversible_migration.rb +4 -8
- data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +75 -0
- data/lib/rubocop/cop/rails/safe_navigation.rb +30 -11
- data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +5 -10
- data/lib/rubocop/cop/rails/save_bang.rb +17 -20
- data/lib/rubocop/cop/rails/scope_args.rb +2 -1
- data/lib/rubocop/cop/rails/short_i18n.rb +7 -9
- data/lib/rubocop/cop/rails/skips_model_validations.rb +4 -4
- data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +5 -6
- data/lib/rubocop/cop/rails/time_zone.rb +35 -25
- data/lib/rubocop/cop/rails/time_zone_assignment.rb +37 -0
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +4 -6
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +4 -2
- data/lib/rubocop/cop/rails/unknown_env.rb +3 -3
- data/lib/rubocop/cop/rails/validation.rb +15 -14
- data/lib/rubocop/cop/rails/where_equals.rb +98 -0
- data/lib/rubocop/cop/rails/where_exists.rb +19 -13
- data/lib/rubocop/cop/rails/where_not.rb +14 -19
- data/lib/rubocop/cop/rails_cops.rb +8 -0
- data/lib/rubocop/rails.rb +2 -0
- data/lib/rubocop/rails/schema_loader.rb +4 -4
- data/lib/rubocop/rails/schema_loader/schema.rb +2 -4
- data/lib/rubocop/rails/version.rb +5 -1
- metadata +29 -14
@@ -17,9 +17,12 @@ module RuboCop
|
|
17
17
|
# # good
|
18
18
|
# enum status: { active: 0, archived: 1 }
|
19
19
|
#
|
20
|
-
class EnumHash <
|
20
|
+
class EnumHash < Base
|
21
|
+
extend AutoCorrector
|
22
|
+
|
21
23
|
MSG = 'Enum defined as an array found in `%<enum>s` enum declaration. '\
|
22
24
|
'Use hash syntax instead.'
|
25
|
+
RESTRICT_ON_SEND = %i[enum].freeze
|
23
26
|
|
24
27
|
def_node_matcher :enum?, <<~PATTERN
|
25
28
|
(send nil? :enum (hash $...))
|
@@ -35,19 +38,17 @@ module RuboCop
|
|
35
38
|
key, array = array_pair?(pair)
|
36
39
|
next unless key
|
37
40
|
|
38
|
-
add_offense(array, message: format(MSG, enum: enum_name(key)))
|
41
|
+
add_offense(array, message: format(MSG, enum: enum_name(key))) do |corrector|
|
42
|
+
hash = array.children.each_with_index.map do |elem, index|
|
43
|
+
"#{source(elem)} => #{index}"
|
44
|
+
end.join(', ')
|
45
|
+
|
46
|
+
corrector.replace(array.loc.expression, "{#{hash}}")
|
47
|
+
end
|
39
48
|
end
|
40
49
|
end
|
41
50
|
end
|
42
51
|
|
43
|
-
def autocorrect(node)
|
44
|
-
hash = node.children.each_with_index.map do |elem, index|
|
45
|
-
"#{source(elem)} => #{index}"
|
46
|
-
end.join(', ')
|
47
|
-
|
48
|
-
->(corrector) { corrector.replace(node.loc.expression, "{#{hash}}") }
|
49
|
-
end
|
50
|
-
|
51
52
|
private
|
52
53
|
|
53
54
|
def enum_name(key)
|
@@ -17,11 +17,12 @@ module RuboCop
|
|
17
17
|
#
|
18
18
|
# # good
|
19
19
|
# enum status: [:active, :archived]
|
20
|
-
class EnumUniqueness <
|
20
|
+
class EnumUniqueness < Base
|
21
21
|
include Duplication
|
22
22
|
|
23
23
|
MSG = 'Duplicate value `%<value>s` found in `%<enum>s` ' \
|
24
24
|
'enum declaration.'
|
25
|
+
RESTRICT_ON_SEND = %i[enum].freeze
|
25
26
|
|
26
27
|
def_node_matcher :enum?, <<~PATTERN
|
27
28
|
(send nil? :enum (hash $...))
|
@@ -15,12 +15,16 @@ module RuboCop
|
|
15
15
|
#
|
16
16
|
# # good
|
17
17
|
# Rails.env.production?
|
18
|
-
class EnvironmentComparison <
|
18
|
+
class EnvironmentComparison < Base
|
19
|
+
extend AutoCorrector
|
20
|
+
|
19
21
|
MSG = 'Favor `%<bang>sRails.env.%<env>s?` over `%<source>s`.'
|
20
22
|
|
21
23
|
SYM_MSG = 'Do not compare `Rails.env` with a symbol, it will always ' \
|
22
24
|
'evaluate to `false`.'
|
23
25
|
|
26
|
+
RESTRICT_ON_SEND = %i[== !=].freeze
|
27
|
+
|
24
28
|
def_node_matcher :comparing_str_env_with_rails_env_on_lhs?, <<~PATTERN
|
25
29
|
(send
|
26
30
|
(send (const {nil? cbase} :Rails) :env)
|
@@ -62,28 +66,28 @@ module RuboCop
|
|
62
66
|
comparing_str_env_with_rails_env_on_rhs?(node))
|
63
67
|
env, = *env_node
|
64
68
|
bang = node.method?(:!=) ? '!' : ''
|
69
|
+
message = format(MSG, bang: bang, env: env, source: node.source)
|
65
70
|
|
66
|
-
add_offense(node, message:
|
67
|
-
|
68
|
-
|
69
|
-
end
|
70
|
-
|
71
|
-
if comparing_sym_env_with_rails_env_on_lhs?(node) ||
|
72
|
-
comparing_sym_env_with_rails_env_on_rhs?(node)
|
73
|
-
add_offense(node, message: SYM_MSG)
|
71
|
+
add_offense(node, message: message) do |corrector|
|
72
|
+
autocorrect(corrector, node)
|
73
|
+
end
|
74
74
|
end
|
75
|
-
end
|
76
75
|
|
77
|
-
|
78
|
-
lambda do |corrector|
|
79
|
-
replacement = build_predicate_method(node)
|
76
|
+
return unless comparing_sym_env_with_rails_env_on_lhs?(node) || comparing_sym_env_with_rails_env_on_rhs?(node)
|
80
77
|
|
81
|
-
|
78
|
+
add_offense(node, message: SYM_MSG) do |corrector|
|
79
|
+
autocorrect(corrector, node)
|
82
80
|
end
|
83
81
|
end
|
84
82
|
|
85
83
|
private
|
86
84
|
|
85
|
+
def autocorrect(corrector, node)
|
86
|
+
replacement = build_predicate_method(node)
|
87
|
+
|
88
|
+
corrector.replace(node.source_range, replacement)
|
89
|
+
end
|
90
|
+
|
87
91
|
def build_predicate_method(node)
|
88
92
|
if rails_env_on_lhs?(node)
|
89
93
|
build_predicate_method_for_rails_env_on_lhs(node)
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop looks for direct access to environment variables through the
|
7
|
+
# `ENV` variable within the application code. This can lead to runtime
|
8
|
+
# errors due to misconfiguration that could have been discovered at boot
|
9
|
+
# time if the environment variables were loaded as part of initialization
|
10
|
+
# and copied into the application's configuration or secrets. The cop can
|
11
|
+
# be configured to allow either reads or writes if required.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# # good
|
15
|
+
# Rails.application.config.foo
|
16
|
+
# Rails.application.config.x.foo.bar
|
17
|
+
# Rails.application.secrets.foo
|
18
|
+
# Rails.application.config.foo = "bar"
|
19
|
+
#
|
20
|
+
# @example AllowReads: false (default)
|
21
|
+
# # bad
|
22
|
+
# ENV["FOO"]
|
23
|
+
# ENV.fetch("FOO")
|
24
|
+
#
|
25
|
+
# @example AllowReads: true
|
26
|
+
# # good
|
27
|
+
# ENV["FOO"]
|
28
|
+
# ENV.fetch("FOO")
|
29
|
+
#
|
30
|
+
# @example AllowWrites: false (default)
|
31
|
+
# # bad
|
32
|
+
# ENV["FOO"] = "bar"
|
33
|
+
#
|
34
|
+
# @example AllowWrites: true
|
35
|
+
# # good
|
36
|
+
# ENV["FOO"] = "bar"
|
37
|
+
class EnvironmentVariableAccess < Base
|
38
|
+
READ_MSG = 'Do not read from `ENV` directly post initialization.'
|
39
|
+
WRITE_MSG = 'Do not write to `ENV` directly post initialization.'
|
40
|
+
|
41
|
+
def on_const(node)
|
42
|
+
add_offense(node, message: READ_MSG) if env_read?(node) && !allow_reads?
|
43
|
+
add_offense(node, message: WRITE_MSG) if env_write?(node) && !allow_writes?
|
44
|
+
end
|
45
|
+
|
46
|
+
def_node_search :env_read?, <<~PATTERN
|
47
|
+
^(send (const {cbase nil?} :ENV) !:[]= ...)
|
48
|
+
PATTERN
|
49
|
+
|
50
|
+
def_node_search :env_write?, <<~PATTERN
|
51
|
+
{^(indexasgn (const {cbase nil?} :ENV) ...)
|
52
|
+
^(send (const {cbase nil?} :ENV) :[]= ...)}
|
53
|
+
PATTERN
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def allow_reads?
|
58
|
+
cop_config['AllowReads'] == true
|
59
|
+
end
|
60
|
+
|
61
|
+
def allow_writes?
|
62
|
+
cop_config['AllowWrites'] == true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -23,27 +23,21 @@ module RuboCop
|
|
23
23
|
#
|
24
24
|
# # good
|
25
25
|
# raise 'a bad error has happened'
|
26
|
-
class Exit <
|
26
|
+
class Exit < Base
|
27
27
|
include ConfigurableEnforcedStyle
|
28
28
|
|
29
29
|
MSG = 'Do not use `exit` in Rails applications.'
|
30
|
-
|
30
|
+
RESTRICT_ON_SEND = %i[exit exit!].freeze
|
31
31
|
EXPLICIT_RECEIVERS = %i[Kernel Process].freeze
|
32
32
|
|
33
33
|
def on_send(node)
|
34
|
-
add_offense(node
|
34
|
+
add_offense(node.loc.selector) if offending_node?(node)
|
35
35
|
end
|
36
36
|
|
37
37
|
private
|
38
38
|
|
39
39
|
def offending_node?(node)
|
40
|
-
|
41
|
-
right_argument_count?(node.arguments) &&
|
42
|
-
right_receiver?(node.receiver)
|
43
|
-
end
|
44
|
-
|
45
|
-
def right_method_name?(method_name)
|
46
|
-
TARGET_METHODS.include?(method_name)
|
40
|
+
right_argument_count?(node.arguments) && right_receiver?(node.receiver)
|
47
41
|
end
|
48
42
|
|
49
43
|
# More than 1 argument likely means it is a different
|
@@ -25,14 +25,13 @@ module RuboCop
|
|
25
25
|
# # good
|
26
26
|
# Rails.root.join('app/models/goober')
|
27
27
|
#
|
28
|
-
class FilePath <
|
28
|
+
class FilePath < Base
|
29
29
|
include ConfigurableEnforcedStyle
|
30
30
|
include RangeHelp
|
31
31
|
|
32
|
-
MSG_SLASHES = '
|
33
|
-
|
34
|
-
|
35
|
-
'instead.'
|
32
|
+
MSG_SLASHES = 'Prefer `Rails.root.join(\'path/to\')`.'
|
33
|
+
MSG_ARGUMENTS = 'Prefer `Rails.root.join(\'path\', \'to\')`.'
|
34
|
+
RESTRICT_ON_SEND = %i[join].freeze
|
36
35
|
|
37
36
|
def_node_matcher :file_join_nodes?, <<~PATTERN
|
38
37
|
(send (const nil? :File) :join ...)
|
@@ -97,10 +96,10 @@ module RuboCop
|
|
97
96
|
line_range = node.loc.column...node.loc.last_column
|
98
97
|
source_range = source_range(processed_source.buffer, node.first_line,
|
99
98
|
line_range)
|
100
|
-
add_offense(
|
99
|
+
add_offense(source_range)
|
101
100
|
end
|
102
101
|
|
103
|
-
def message(
|
102
|
+
def message(_range)
|
104
103
|
format(style == :arguments ? MSG_ARGUMENTS : MSG_SLASHES)
|
105
104
|
end
|
106
105
|
end
|
@@ -13,11 +13,12 @@ module RuboCop
|
|
13
13
|
#
|
14
14
|
# # good
|
15
15
|
# User.find_by(name: 'Bruce')
|
16
|
-
class FindBy <
|
16
|
+
class FindBy < Base
|
17
17
|
include RangeHelp
|
18
|
+
extend AutoCorrector
|
18
19
|
|
19
20
|
MSG = 'Use `find_by` instead of `where.%<method>s`.'
|
20
|
-
|
21
|
+
RESTRICT_ON_SEND = %i[first take].freeze
|
21
22
|
|
22
23
|
def_node_matcher :where_first?, <<~PATTERN
|
23
24
|
(send ({send csend} _ :where ...) {:first :take})
|
@@ -26,28 +27,27 @@ module RuboCop
|
|
26
27
|
def on_send(node)
|
27
28
|
return unless where_first?(node)
|
28
29
|
|
29
|
-
range = range_between(node.receiver.loc.selector.begin_pos,
|
30
|
-
node.loc.selector.end_pos)
|
30
|
+
range = range_between(node.receiver.loc.selector.begin_pos, node.loc.selector.end_pos)
|
31
31
|
|
32
|
-
add_offense(
|
33
|
-
|
32
|
+
add_offense(range, message: format(MSG, method: node.method_name)) do |corrector|
|
33
|
+
autocorrect(corrector, node)
|
34
|
+
end
|
34
35
|
end
|
35
36
|
alias on_csend on_send
|
36
37
|
|
37
|
-
|
38
|
+
private
|
39
|
+
|
40
|
+
def autocorrect(corrector, node)
|
38
41
|
# Don't autocorrect where(...).first, because it can return different
|
39
42
|
# results from find_by. (They order records differently, so the
|
40
43
|
# 'first' record can be different.)
|
41
44
|
return if node.method?(:first)
|
42
45
|
|
43
46
|
where_loc = node.receiver.loc.selector
|
44
|
-
first_loc = range_between(node.loc.dot.begin_pos,
|
45
|
-
node.loc.selector.end_pos)
|
47
|
+
first_loc = range_between(node.loc.dot.begin_pos, node.loc.selector.end_pos)
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
corrector.replace(first_loc, '')
|
50
|
-
end
|
49
|
+
corrector.replace(where_loc, 'find_by')
|
50
|
+
corrector.replace(first_loc, '')
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
@@ -16,10 +16,12 @@ module RuboCop
|
|
16
16
|
# # good
|
17
17
|
# User.find(id)
|
18
18
|
#
|
19
|
-
class FindById <
|
19
|
+
class FindById < Base
|
20
20
|
include RangeHelp
|
21
|
+
extend AutoCorrector
|
21
22
|
|
22
23
|
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
24
|
+
RESTRICT_ON_SEND = %i[take! find_by_id! find_by!].freeze
|
23
25
|
|
24
26
|
def_node_matcher :where_take?, <<~PATTERN
|
25
27
|
(send
|
@@ -38,41 +40,30 @@ module RuboCop
|
|
38
40
|
def on_send(node)
|
39
41
|
where_take?(node) do |where, id_value|
|
40
42
|
range = where_take_offense_range(node, where)
|
41
|
-
|
42
|
-
good_method = build_good_method(id_value)
|
43
43
|
bad_method = build_where_take_bad_method(id_value)
|
44
|
-
message = format(MSG, good_method: good_method, bad_method: bad_method)
|
45
44
|
|
46
|
-
|
45
|
+
register_offense(range, id_value, bad_method)
|
47
46
|
end
|
48
47
|
|
49
48
|
find_by?(node) do |id_value|
|
50
49
|
range = find_by_offense_range(node)
|
51
|
-
|
52
|
-
good_method = build_good_method(id_value)
|
53
50
|
bad_method = build_find_by_bad_method(node, id_value)
|
54
|
-
message = format(MSG, good_method: good_method, bad_method: bad_method)
|
55
51
|
|
56
|
-
|
52
|
+
register_offense(range, id_value, bad_method)
|
57
53
|
end
|
58
54
|
end
|
59
55
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
range = find_by_offense_range(node)
|
66
|
-
end
|
56
|
+
private
|
57
|
+
|
58
|
+
def register_offense(range, id_value, bad_method)
|
59
|
+
good_method = build_good_method(id_value)
|
60
|
+
message = format(MSG, good_method: good_method, bad_method: bad_method)
|
67
61
|
|
68
|
-
|
69
|
-
|
70
|
-
corrector.replace(range, replacement)
|
62
|
+
add_offense(range, message: message) do |corrector|
|
63
|
+
corrector.replace(range, good_method)
|
71
64
|
end
|
72
65
|
end
|
73
66
|
|
74
|
-
private
|
75
|
-
|
76
67
|
def where_take_offense_range(node, where)
|
77
68
|
range_between(where.loc.selector.begin_pos, node.loc.expression.end_pos)
|
78
69
|
end
|
@@ -12,38 +12,39 @@ module RuboCop
|
|
12
12
|
#
|
13
13
|
# # good
|
14
14
|
# User.all.find_each
|
15
|
-
|
15
|
+
#
|
16
|
+
# @example IgnoredMethods: ['order']
|
17
|
+
# # good
|
18
|
+
# User.order(:foo).each
|
19
|
+
class FindEach < Base
|
20
|
+
include ActiveRecordHelper
|
21
|
+
extend AutoCorrector
|
22
|
+
|
16
23
|
MSG = 'Use `find_each` instead of `each`.'
|
24
|
+
RESTRICT_ON_SEND = %i[each].freeze
|
17
25
|
|
18
26
|
SCOPE_METHODS = %i[
|
19
27
|
all eager_load includes joins left_joins left_outer_joins not preload
|
20
28
|
references unscoped where
|
21
29
|
].freeze
|
22
|
-
IGNORED_METHODS = %i[order limit select].freeze
|
23
30
|
|
24
31
|
def on_send(node)
|
25
|
-
return unless node.receiver&.send_type?
|
26
|
-
node.method?(:each)
|
27
|
-
|
32
|
+
return unless node.receiver&.send_type?
|
28
33
|
return unless SCOPE_METHODS.include?(node.receiver.method_name)
|
29
|
-
return if
|
34
|
+
return if node.receiver.receiver.nil? && !inherit_active_record_base?(node)
|
35
|
+
return if ignored?(node)
|
30
36
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
->(corrector) { corrector.replace(node.loc.selector, 'find_each') }
|
37
|
+
range = node.loc.selector
|
38
|
+
add_offense(range) do |corrector|
|
39
|
+
corrector.replace(range, 'find_each')
|
40
|
+
end
|
36
41
|
end
|
37
42
|
|
38
43
|
private
|
39
44
|
|
40
|
-
def
|
41
|
-
node.each_node(:send).map(&:method_name)
|
42
|
-
|
43
|
-
|
44
|
-
def ignored_by_find_each?(relation_method)
|
45
|
-
# Active Record's #find_each ignores various extra parameters
|
46
|
-
IGNORED_METHODS.include?(relation_method)
|
45
|
+
def ignored?(node)
|
46
|
+
method_chain = node.each_node(:send).map(&:method_name)
|
47
|
+
(cop_config['IgnoredMethods'].map(&:to_sym) & method_chain).any?
|
47
48
|
end
|
48
49
|
end
|
49
50
|
end
|
@@ -11,13 +11,14 @@ module RuboCop
|
|
11
11
|
#
|
12
12
|
# # good
|
13
13
|
# # has_many :ingredients, through: :recipe_ingredients
|
14
|
-
class HasAndBelongsToMany <
|
14
|
+
class HasAndBelongsToMany < Base
|
15
15
|
MSG = 'Prefer `has_many :through` to `has_and_belongs_to_many`.'
|
16
|
+
RESTRICT_ON_SEND = %i[has_and_belongs_to_many].freeze
|
16
17
|
|
17
18
|
def on_send(node)
|
18
19
|
return unless node.command?(:has_and_belongs_to_many)
|
19
20
|
|
20
|
-
add_offense(node
|
21
|
+
add_offense(node.loc.selector)
|
21
22
|
end
|
22
23
|
end
|
23
24
|
end
|
@@ -5,7 +5,9 @@ module RuboCop
|
|
5
5
|
module Rails
|
6
6
|
# This cop looks for `has_many` or `has_one` associations that don't
|
7
7
|
# specify a `:dependent` option.
|
8
|
-
#
|
8
|
+
#
|
9
|
+
# It doesn't register an offense if `:through` or `dependent: nil`
|
10
|
+
# is specified, or if the model is read-only.
|
9
11
|
#
|
10
12
|
# @example
|
11
13
|
# # bad
|
@@ -18,10 +20,21 @@ module RuboCop
|
|
18
20
|
# class User < ActiveRecord::Base
|
19
21
|
# has_many :comments, dependent: :restrict_with_exception
|
20
22
|
# has_one :avatar, dependent: :destroy
|
23
|
+
# has_many :articles, dependent: nil
|
21
24
|
# has_many :patients, through: :appointments
|
22
25
|
# end
|
23
|
-
|
26
|
+
#
|
27
|
+
# class User < ActiveRecord::Base
|
28
|
+
# has_many :comments
|
29
|
+
# has_one :avatar
|
30
|
+
#
|
31
|
+
# def readonly?
|
32
|
+
# true
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
class HasManyOrHasOneDependent < Base
|
24
36
|
MSG = 'Specify a `:dependent` option.'
|
37
|
+
RESTRICT_ON_SEND = %i[has_many has_one].freeze
|
25
38
|
|
26
39
|
def_node_search :active_resource_class?, <<~PATTERN
|
27
40
|
(const (const nil? :ActiveResource) :Base)
|
@@ -36,7 +49,7 @@ module RuboCop
|
|
36
49
|
PATTERN
|
37
50
|
|
38
51
|
def_node_matcher :dependent_option?, <<~PATTERN
|
39
|
-
(pair (sym :dependent) !nil)
|
52
|
+
(pair (sym :dependent) {!nil (nil)})
|
40
53
|
PATTERN
|
41
54
|
|
42
55
|
def_node_matcher :present_option?, <<~PATTERN
|
@@ -50,20 +63,38 @@ module RuboCop
|
|
50
63
|
(args) ...)
|
51
64
|
PATTERN
|
52
65
|
|
66
|
+
def_node_matcher :association_extension_block?, <<~PATTERN
|
67
|
+
(block
|
68
|
+
(send nil? :has_many _)
|
69
|
+
(args) ...)
|
70
|
+
PATTERN
|
71
|
+
|
72
|
+
def_node_matcher :readonly?, <<~PATTERN
|
73
|
+
(def :readonly?
|
74
|
+
(args)
|
75
|
+
(true))
|
76
|
+
PATTERN
|
77
|
+
|
53
78
|
def on_send(node)
|
54
|
-
return if active_resource?(node.parent)
|
79
|
+
return if active_resource?(node.parent) || readonly_model?(node)
|
55
80
|
return if !association_without_options?(node) && valid_options?(association_with_options?(node))
|
56
81
|
return if valid_options_in_with_options_block?(node)
|
57
82
|
|
58
|
-
add_offense(node
|
83
|
+
add_offense(node.loc.selector)
|
59
84
|
end
|
60
85
|
|
61
86
|
private
|
62
87
|
|
88
|
+
def readonly_model?(node)
|
89
|
+
return false unless (parent = node.parent)
|
90
|
+
|
91
|
+
parent.each_descendant(:def).any? { |def_node| readonly?(def_node) }
|
92
|
+
end
|
93
|
+
|
63
94
|
def valid_options_in_with_options_block?(node)
|
64
95
|
return true unless node.parent
|
65
96
|
|
66
|
-
n = node.parent.begin_type? ? node.parent.parent : node.parent
|
97
|
+
n = node.parent.begin_type? || association_extension_block?(node.parent) ? node.parent.parent : node.parent
|
67
98
|
|
68
99
|
contain_valid_options_in_with_options_block?(n)
|
69
100
|
end
|