rubocop-rails 2.4.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 +7 -0
- data/LICENSE.txt +20 -0
- data/README.md +92 -0
- data/bin/setup +7 -0
- data/config/default.yml +510 -0
- data/lib/rubocop-rails.rb +12 -0
- data/lib/rubocop/cop/mixin/target_rails_version.rb +16 -0
- data/lib/rubocop/cop/rails/action_filter.rb +111 -0
- data/lib/rubocop/cop/rails/active_record_aliases.rb +48 -0
- data/lib/rubocop/cop/rails/active_record_override.rb +82 -0
- data/lib/rubocop/cop/rails/active_support_aliases.rb +69 -0
- data/lib/rubocop/cop/rails/application_controller.rb +36 -0
- data/lib/rubocop/cop/rails/application_job.rb +40 -0
- data/lib/rubocop/cop/rails/application_mailer.rb +40 -0
- data/lib/rubocop/cop/rails/application_record.rb +40 -0
- data/lib/rubocop/cop/rails/assert_not.rb +44 -0
- data/lib/rubocop/cop/rails/belongs_to.rb +102 -0
- data/lib/rubocop/cop/rails/blank.rb +164 -0
- data/lib/rubocop/cop/rails/bulk_change_table.rb +293 -0
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +91 -0
- data/lib/rubocop/cop/rails/date.rb +161 -0
- data/lib/rubocop/cop/rails/delegate.rb +132 -0
- data/lib/rubocop/cop/rails/delegate_allow_blank.rb +37 -0
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +91 -0
- data/lib/rubocop/cop/rails/enum_hash.rb +75 -0
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +65 -0
- data/lib/rubocop/cop/rails/environment_comparison.rb +68 -0
- data/lib/rubocop/cop/rails/exit.rb +67 -0
- data/lib/rubocop/cop/rails/file_path.rb +108 -0
- data/lib/rubocop/cop/rails/find_by.rb +55 -0
- data/lib/rubocop/cop/rails/find_each.rb +51 -0
- data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +25 -0
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +106 -0
- data/lib/rubocop/cop/rails/helper_instance_variable.rb +39 -0
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +117 -0
- data/lib/rubocop/cop/rails/http_status.rb +160 -0
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +94 -0
- data/lib/rubocop/cop/rails/inverse_of.rb +246 -0
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +175 -0
- data/lib/rubocop/cop/rails/link_to_blank.rb +98 -0
- data/lib/rubocop/cop/rails/not_null_column.rb +67 -0
- data/lib/rubocop/cop/rails/output.rb +49 -0
- data/lib/rubocop/cop/rails/output_safety.rb +99 -0
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +107 -0
- data/lib/rubocop/cop/rails/presence.rb +148 -0
- data/lib/rubocop/cop/rails/present.rb +153 -0
- data/lib/rubocop/cop/rails/rake_environment.rb +91 -0
- data/lib/rubocop/cop/rails/read_write_attribute.rb +74 -0
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +111 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +136 -0
- data/lib/rubocop/cop/rails/reflection_class_name.rb +37 -0
- data/lib/rubocop/cop/rails/refute_methods.rb +76 -0
- data/lib/rubocop/cop/rails/relative_date_constant.rb +102 -0
- data/lib/rubocop/cop/rails/request_referer.rb +56 -0
- data/lib/rubocop/cop/rails/reversible_migration.rb +284 -0
- data/lib/rubocop/cop/rails/safe_navigation.rb +85 -0
- data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +48 -0
- data/lib/rubocop/cop/rails/save_bang.rb +331 -0
- data/lib/rubocop/cop/rails/scope_args.rb +29 -0
- data/lib/rubocop/cop/rails/skips_model_validations.rb +87 -0
- data/lib/rubocop/cop/rails/time_zone.rb +249 -0
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +105 -0
- data/lib/rubocop/cop/rails/unknown_env.rb +84 -0
- data/lib/rubocop/cop/rails/validation.rb +147 -0
- data/lib/rubocop/cop/rails_cops.rb +61 -0
- data/lib/rubocop/rails.rb +12 -0
- data/lib/rubocop/rails/inject.rb +18 -0
- data/lib/rubocop/rails/version.rb +10 -0
- metadata +148 -0
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop looks for enums written with array syntax.
|
7
|
+
#
|
8
|
+
# When using array syntax, adding an element in a
|
9
|
+
# position other than the last causes all previous
|
10
|
+
# definitions to shift. Explicitly specifying the
|
11
|
+
# value for each key prevents this from happening.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# # bad
|
15
|
+
# enum status: [:active, :archived]
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# enum status: { active: 0, archived: 1 }
|
19
|
+
#
|
20
|
+
class EnumHash < Cop
|
21
|
+
MSG = 'Enum defined as an array found in `%<enum>s` enum declaration. '\
|
22
|
+
'Use hash syntax instead.'
|
23
|
+
|
24
|
+
def_node_matcher :enum?, <<~PATTERN
|
25
|
+
(send nil? :enum (hash $...))
|
26
|
+
PATTERN
|
27
|
+
|
28
|
+
def_node_matcher :array_pair?, <<~PATTERN
|
29
|
+
(pair $_ $array)
|
30
|
+
PATTERN
|
31
|
+
|
32
|
+
def on_send(node)
|
33
|
+
enum?(node) do |pairs|
|
34
|
+
pairs.each do |pair|
|
35
|
+
key, array = array_pair?(pair)
|
36
|
+
next unless key
|
37
|
+
|
38
|
+
add_offense(array, message: format(MSG, enum: enum_name(key)))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
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
|
+
private
|
52
|
+
|
53
|
+
def enum_name(key)
|
54
|
+
case key.type
|
55
|
+
when :sym, :str
|
56
|
+
key.value
|
57
|
+
else
|
58
|
+
key.source
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def source(elem)
|
63
|
+
case elem.type
|
64
|
+
when :str
|
65
|
+
elem.value.dump
|
66
|
+
when :sym
|
67
|
+
elem.value.inspect
|
68
|
+
else
|
69
|
+
elem.source
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop looks for duplicate values in enum declarations.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# enum status: { active: 0, archived: 0 }
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# enum status: { active: 0, archived: 1 }
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# enum status: [:active, :archived, :active]
|
17
|
+
#
|
18
|
+
# # good
|
19
|
+
# enum status: [:active, :archived]
|
20
|
+
class EnumUniqueness < Cop
|
21
|
+
include Duplication
|
22
|
+
|
23
|
+
MSG = 'Duplicate value `%<value>s` found in `%<enum>s` ' \
|
24
|
+
'enum declaration.'
|
25
|
+
|
26
|
+
def_node_matcher :enum?, <<~PATTERN
|
27
|
+
(send nil? :enum (hash $...))
|
28
|
+
PATTERN
|
29
|
+
|
30
|
+
def_node_matcher :enum_values, <<~PATTERN
|
31
|
+
(pair $_ ${array hash})
|
32
|
+
PATTERN
|
33
|
+
|
34
|
+
def on_send(node)
|
35
|
+
enum?(node) do |pairs|
|
36
|
+
pairs.each do |pair|
|
37
|
+
enum_values(pair) do |key, args|
|
38
|
+
items = args.values
|
39
|
+
|
40
|
+
next unless duplicates?(items)
|
41
|
+
|
42
|
+
consecutive_duplicates(items).each do |item|
|
43
|
+
add_offense(item, message: format(
|
44
|
+
MSG, value: item.source, enum: enum_name(key)
|
45
|
+
))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def enum_name(key)
|
55
|
+
case key.type
|
56
|
+
when :sym, :str
|
57
|
+
key.value
|
58
|
+
else
|
59
|
+
key.source
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks that Rails.env is compared using `.production?`-like
|
7
|
+
# methods instead of equality against a string or symbol.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# Rails.env == 'production'
|
12
|
+
#
|
13
|
+
# # bad, always returns false
|
14
|
+
# Rails.env == :test
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# Rails.env.production?
|
18
|
+
class EnvironmentComparison < Cop
|
19
|
+
MSG = "Favor `Rails.env.%<env>s?` over `Rails.env == '%<env>s'`."
|
20
|
+
|
21
|
+
SYM_MSG = 'Do not compare `Rails.env` with a symbol, it will always ' \
|
22
|
+
'evaluate to `false`.'
|
23
|
+
|
24
|
+
def_node_matcher :environment_str_comparison?, <<~PATTERN
|
25
|
+
(send
|
26
|
+
(send (const {nil? cbase} :Rails) :env)
|
27
|
+
:==
|
28
|
+
$str
|
29
|
+
)
|
30
|
+
PATTERN
|
31
|
+
|
32
|
+
def_node_matcher :environment_sym_comparison?, <<~PATTERN
|
33
|
+
(send
|
34
|
+
(send (const {nil? cbase} :Rails) :env)
|
35
|
+
:==
|
36
|
+
$sym
|
37
|
+
)
|
38
|
+
PATTERN
|
39
|
+
|
40
|
+
def on_send(node)
|
41
|
+
environment_str_comparison?(node) do |env_node|
|
42
|
+
env, = *env_node
|
43
|
+
add_offense(node, message: format(MSG, env: env))
|
44
|
+
end
|
45
|
+
environment_sym_comparison?(node) do |_|
|
46
|
+
add_offense(node, message: SYM_MSG)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def autocorrect(node)
|
51
|
+
lambda do |corrector|
|
52
|
+
corrector.replace(node.source_range, replacement(node))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def replacement(node)
|
59
|
+
"#{node.receiver.source}.#{content(node.first_argument)}?"
|
60
|
+
end
|
61
|
+
|
62
|
+
def_node_matcher :content, <<~PATTERN
|
63
|
+
({str sym} $_)
|
64
|
+
PATTERN
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop enforces that `exit` calls are not used within a rails app.
|
7
|
+
# Valid options are instead to raise an error, break, return, or some
|
8
|
+
# other form of stopping execution of current request.
|
9
|
+
#
|
10
|
+
# There are two obvious cases where `exit` is particularly harmful:
|
11
|
+
#
|
12
|
+
# - Usage in library code for your application. Even though Rails will
|
13
|
+
# rescue from a `SystemExit` and continue on, unit testing that library
|
14
|
+
# code will result in specs exiting (potentially silently if `exit(0)`
|
15
|
+
# is used.)
|
16
|
+
# - Usage in application code outside of the web process could result in
|
17
|
+
# the program exiting, which could result in the code failing to run and
|
18
|
+
# do its job.
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# # bad
|
22
|
+
# exit(0)
|
23
|
+
#
|
24
|
+
# # good
|
25
|
+
# raise 'a bad error has happened'
|
26
|
+
class Exit < Cop
|
27
|
+
include ConfigurableEnforcedStyle
|
28
|
+
|
29
|
+
MSG = 'Do not use `exit` in Rails applications.'
|
30
|
+
TARGET_METHODS = %i[exit exit!].freeze
|
31
|
+
EXPLICIT_RECEIVERS = %i[Kernel Process].freeze
|
32
|
+
|
33
|
+
def on_send(node)
|
34
|
+
add_offense(node, location: :selector) if offending_node?(node)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def offending_node?(node)
|
40
|
+
right_method_name?(node.method_name) &&
|
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)
|
47
|
+
end
|
48
|
+
|
49
|
+
# More than 1 argument likely means it is a different
|
50
|
+
# `exit` implementation than the one we are preventing.
|
51
|
+
def right_argument_count?(arg_nodes)
|
52
|
+
arg_nodes.size <= 1
|
53
|
+
end
|
54
|
+
|
55
|
+
# Only register if exit is being called explicitly on `Kernel`,
|
56
|
+
# `Process`, or if receiver node is nil for plain `exit` calls.
|
57
|
+
def right_receiver?(receiver_node)
|
58
|
+
return true unless receiver_node
|
59
|
+
|
60
|
+
_a, receiver_node_class, _c = *receiver_node
|
61
|
+
|
62
|
+
EXPLICIT_RECEIVERS.include?(receiver_node_class)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop is used to identify usages of file path joining process
|
7
|
+
# to use `Rails.root.join` clause. It is used to add uniformity when
|
8
|
+
# joining paths.
|
9
|
+
#
|
10
|
+
# @example EnforcedStyle: arguments
|
11
|
+
# # bad
|
12
|
+
# Rails.root.join('app/models/goober')
|
13
|
+
# File.join(Rails.root, 'app/models/goober')
|
14
|
+
# "#{Rails.root}/app/models/goober"
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# Rails.root.join('app', 'models', 'goober')
|
18
|
+
#
|
19
|
+
# @example EnforcedStyle: slashes (default)
|
20
|
+
# # bad
|
21
|
+
# Rails.root.join('app', 'models', 'goober')
|
22
|
+
# File.join(Rails.root, 'app/models/goober')
|
23
|
+
# "#{Rails.root}/app/models/goober"
|
24
|
+
#
|
25
|
+
# # good
|
26
|
+
# Rails.root.join('app/models/goober')
|
27
|
+
#
|
28
|
+
class FilePath < Cop
|
29
|
+
include ConfigurableEnforcedStyle
|
30
|
+
include RangeHelp
|
31
|
+
|
32
|
+
MSG_SLASHES = 'Please use `Rails.root.join(\'path/to\')` ' \
|
33
|
+
'instead.'
|
34
|
+
MSG_ARGUMENTS = 'Please use `Rails.root.join(\'path\', \'to\')` ' \
|
35
|
+
'instead.'
|
36
|
+
|
37
|
+
def_node_matcher :file_join_nodes?, <<~PATTERN
|
38
|
+
(send (const nil? :File) :join ...)
|
39
|
+
PATTERN
|
40
|
+
|
41
|
+
def_node_search :rails_root_nodes?, <<~PATTERN
|
42
|
+
(send (const nil? :Rails) :root)
|
43
|
+
PATTERN
|
44
|
+
|
45
|
+
def_node_matcher :rails_root_join_nodes?, <<~PATTERN
|
46
|
+
(send (send (const nil? :Rails) :root) :join ...)
|
47
|
+
PATTERN
|
48
|
+
|
49
|
+
def on_dstr(node)
|
50
|
+
return unless rails_root_nodes?(node)
|
51
|
+
return unless node.children.last.source.start_with?('.') ||
|
52
|
+
node.children.last.source.include?(File::SEPARATOR)
|
53
|
+
|
54
|
+
register_offense(node)
|
55
|
+
end
|
56
|
+
|
57
|
+
def on_send(node)
|
58
|
+
check_for_file_join_with_rails_root(node)
|
59
|
+
check_for_rails_root_join_with_slash_separated_path(node)
|
60
|
+
check_for_rails_root_join_with_string_arguments(node)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def check_for_file_join_with_rails_root(node)
|
66
|
+
return unless file_join_nodes?(node)
|
67
|
+
return unless node.arguments.any? { |e| rails_root_nodes?(e) }
|
68
|
+
|
69
|
+
register_offense(node)
|
70
|
+
end
|
71
|
+
|
72
|
+
def check_for_rails_root_join_with_string_arguments(node)
|
73
|
+
return unless style == :slashes
|
74
|
+
return unless rails_root_nodes?(node)
|
75
|
+
return unless rails_root_join_nodes?(node)
|
76
|
+
return unless node.arguments.size > 1
|
77
|
+
return unless node.arguments.all?(&:str_type?)
|
78
|
+
|
79
|
+
register_offense(node)
|
80
|
+
end
|
81
|
+
|
82
|
+
def check_for_rails_root_join_with_slash_separated_path(node)
|
83
|
+
return unless style == :arguments
|
84
|
+
return unless rails_root_nodes?(node)
|
85
|
+
return unless rails_root_join_nodes?(node)
|
86
|
+
return unless node.arguments.any? { |arg| string_with_slash?(arg) }
|
87
|
+
|
88
|
+
register_offense(node)
|
89
|
+
end
|
90
|
+
|
91
|
+
def string_with_slash?(node)
|
92
|
+
node.str_type? && node.source =~ %r{/}
|
93
|
+
end
|
94
|
+
|
95
|
+
def register_offense(node)
|
96
|
+
line_range = node.loc.column...node.loc.last_column
|
97
|
+
source_range = source_range(processed_source.buffer, node.first_line,
|
98
|
+
line_range)
|
99
|
+
add_offense(node, location: source_range)
|
100
|
+
end
|
101
|
+
|
102
|
+
def message(_node)
|
103
|
+
format(style == :arguments ? MSG_ARGUMENTS : MSG_SLASHES)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop is used to identify usages of `where.first` and
|
7
|
+
# change them to use `find_by` instead.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# User.where(name: 'Bruce').first
|
12
|
+
# User.where(name: 'Bruce').take
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# User.find_by(name: 'Bruce')
|
16
|
+
class FindBy < Cop
|
17
|
+
include RangeHelp
|
18
|
+
|
19
|
+
MSG = 'Use `find_by` instead of `where.%<method>s`.'
|
20
|
+
TARGET_SELECTORS = %i[first take].freeze
|
21
|
+
|
22
|
+
def_node_matcher :where_first?, <<~PATTERN
|
23
|
+
(send ({send csend} _ :where ...) {:first :take})
|
24
|
+
PATTERN
|
25
|
+
|
26
|
+
def on_send(node)
|
27
|
+
return unless where_first?(node)
|
28
|
+
|
29
|
+
range = range_between(node.receiver.loc.selector.begin_pos,
|
30
|
+
node.loc.selector.end_pos)
|
31
|
+
|
32
|
+
add_offense(node, location: range,
|
33
|
+
message: format(MSG, method: node.method_name))
|
34
|
+
end
|
35
|
+
alias on_csend on_send
|
36
|
+
|
37
|
+
def autocorrect(node)
|
38
|
+
# Don't autocorrect where(...).first, because it can return different
|
39
|
+
# results from find_by. (They order records differently, so the
|
40
|
+
# 'first' record can be different.)
|
41
|
+
return if node.method?(:first)
|
42
|
+
|
43
|
+
where_loc = node.receiver.loc.selector
|
44
|
+
first_loc = range_between(node.loc.dot.begin_pos,
|
45
|
+
node.loc.selector.end_pos)
|
46
|
+
|
47
|
+
lambda do |corrector|
|
48
|
+
corrector.replace(where_loc, 'find_by')
|
49
|
+
corrector.replace(first_loc, '')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop is used to identify usages of `all.each` and
|
7
|
+
# change them to use `all.find_each` instead.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# User.all.each
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# User.all.find_each
|
15
|
+
class FindEach < Cop
|
16
|
+
MSG = 'Use `find_each` instead of `each`.'
|
17
|
+
|
18
|
+
SCOPE_METHODS = %i[
|
19
|
+
all eager_load includes joins left_joins left_outer_joins not preload
|
20
|
+
references unscoped where
|
21
|
+
].freeze
|
22
|
+
IGNORED_METHODS = %i[order limit select].freeze
|
23
|
+
|
24
|
+
def on_send(node)
|
25
|
+
return unless node.receiver&.send_type? &&
|
26
|
+
node.method?(:each)
|
27
|
+
|
28
|
+
return unless SCOPE_METHODS.include?(node.receiver.method_name)
|
29
|
+
return if method_chain(node).any? { |m| ignored_by_find_each?(m) }
|
30
|
+
|
31
|
+
add_offense(node, location: :selector)
|
32
|
+
end
|
33
|
+
|
34
|
+
def autocorrect(node)
|
35
|
+
->(corrector) { corrector.replace(node.loc.selector, 'find_each') }
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def method_chain(node)
|
41
|
+
node.each_node(:send).map(&:method_name)
|
42
|
+
end
|
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)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|