rubocop 0.52.0 → 0.52.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -5
- data/config/default.yml +1 -11
- data/config/disabled.yml +5 -0
- data/config/enabled.yml +6 -8
- data/lib/rubocop.rb +13 -2
- data/lib/rubocop/ast/node.rb +23 -15
- data/lib/rubocop/cli.rb +25 -2
- data/lib/rubocop/config.rb +23 -8
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -2
- data/lib/rubocop/cop/bundler/ordered_gems.rb +9 -0
- data/lib/rubocop/cop/commissioner.rb +1 -1
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +121 -0
- data/lib/rubocop/cop/correctors/condition_corrector.rb +28 -0
- data/lib/rubocop/cop/correctors/empty_line_corrector.rb +26 -0
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +62 -0
- data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +44 -0
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +31 -0
- data/lib/rubocop/cop/correctors/punctuation_corrector.rb +29 -0
- data/lib/rubocop/cop/correctors/space_corrector.rb +34 -0
- data/lib/rubocop/cop/correctors/string_literal_corrector.rb +25 -0
- data/lib/rubocop/cop/correctors/unused_arg_corrector.rb +31 -0
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +9 -0
- data/lib/rubocop/cop/generator.rb +18 -87
- data/lib/rubocop/cop/generator/require_file_injector.rb +78 -0
- data/lib/rubocop/cop/layout/access_modifier_indentation.rb +5 -1
- data/lib/rubocop/cop/layout/align_array.rb +5 -1
- data/lib/rubocop/cop/layout/align_hash.rb +1 -1
- data/lib/rubocop/cop/layout/align_parameters.rb +5 -1
- data/lib/rubocop/cop/layout/case_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/class_structure.rb +2 -2
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +5 -1
- data/lib/rubocop/cop/layout/comment_indentation.rb +5 -1
- data/lib/rubocop/cop/layout/else_alignment.rb +5 -1
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +3 -3
- data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +17 -19
- data/lib/rubocop/cop/layout/empty_lines_around_begin_body.rb +4 -0
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +4 -0
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +4 -0
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +4 -0
- data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +4 -0
- data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +4 -0
- data/lib/rubocop/cop/layout/first_array_element_line_break.rb +4 -0
- data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +4 -0
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +4 -0
- data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +4 -0
- data/lib/rubocop/cop/layout/first_parameter_indentation.rb +6 -2
- data/lib/rubocop/cop/layout/indent_array.rb +6 -2
- data/lib/rubocop/cop/layout/indent_assignment.rb +6 -2
- data/lib/rubocop/cop/layout/indent_hash.rb +5 -1
- data/lib/rubocop/cop/layout/indentation_consistency.rb +5 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +5 -1
- data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +4 -0
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +3 -3
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -2
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +4 -0
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +4 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +6 -2
- data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +4 -0
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +6 -2
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -1
- data/lib/rubocop/cop/layout/space_after_comma.rb +4 -0
- data/lib/rubocop/cop/layout/space_after_semicolon.rb +4 -0
- data/lib/rubocop/cop/layout/space_before_comma.rb +4 -0
- data/lib/rubocop/cop/layout/space_before_semicolon.rb +4 -0
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +3 -2
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +16 -7
- data/lib/rubocop/cop/layout/tab.rb +1 -1
- data/lib/rubocop/cop/lint/block_alignment.rb +1 -1
- data/lib/rubocop/cop/lint/def_end_alignment.rb +2 -2
- data/lib/rubocop/cop/lint/end_alignment.rb +3 -1
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -1
- data/lib/rubocop/cop/lint/unused_block_argument.rb +4 -0
- data/lib/rubocop/cop/lint/unused_method_argument.rb +6 -0
- data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -1
- data/lib/rubocop/cop/mixin/alignment.rb +70 -0
- data/lib/rubocop/cop/mixin/array_hash_indentation.rb +2 -0
- data/lib/rubocop/cop/mixin/array_syntax.rb +2 -0
- data/lib/rubocop/cop/mixin/code_length.rb +2 -0
- data/lib/rubocop/cop/mixin/configurable_max.rb +2 -0
- data/lib/rubocop/cop/mixin/def_node.rb +3 -1
- data/lib/rubocop/cop/mixin/documentation_comment.rb +2 -2
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +3 -15
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -25
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +0 -6
- data/lib/rubocop/cop/mixin/first_element_line_break.rb +5 -9
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +2 -2
- data/lib/rubocop/cop/mixin/ignored_pattern.rb +2 -0
- data/lib/rubocop/cop/mixin/integer_node.rb +2 -0
- data/lib/rubocop/cop/mixin/match_range.rb +2 -0
- data/lib/rubocop/cop/mixin/min_body_length.rb +2 -0
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -0
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +9 -48
- data/lib/rubocop/cop/mixin/negative_conditional.rb +2 -16
- data/lib/rubocop/cop/mixin/ordered_gem_node.rb +12 -31
- data/lib/rubocop/cop/mixin/parentheses.rb +2 -19
- data/lib/rubocop/cop/mixin/percent_literal.rb +3 -3
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +2 -0
- data/lib/rubocop/cop/mixin/rescue_node.rb +2 -0
- data/lib/rubocop/cop/mixin/safe_assignment.rb +2 -0
- data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -3
- data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -3
- data/lib/rubocop/cop/mixin/statement_modifier.rb +4 -2
- data/lib/rubocop/cop/mixin/string_help.rb +2 -0
- data/lib/rubocop/cop/mixin/string_literals_help.rb +2 -13
- data/lib/rubocop/cop/mixin/surrounding_space.rb +4 -21
- data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -10
- data/lib/rubocop/cop/mixin/unused_argument.rb +2 -15
- data/lib/rubocop/cop/performance/case_when_splat.rb +1 -1
- data/lib/rubocop/cop/rails/action_filter.rb +3 -2
- data/lib/rubocop/cop/rails/active_support_aliases.rb +3 -2
- data/lib/rubocop/cop/rails/application_job.rb +6 -0
- data/lib/rubocop/cop/rails/application_record.rb +6 -0
- data/lib/rubocop/cop/rails/blank.rb +10 -9
- data/lib/rubocop/cop/rails/date.rb +22 -14
- data/lib/rubocop/cop/rails/delegate.rb +1 -1
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +3 -2
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +4 -2
- data/lib/rubocop/cop/rails/environment_comparison.rb +2 -2
- data/lib/rubocop/cop/rails/file_path.rb +1 -1
- data/lib/rubocop/cop/rails/find_by.rb +2 -2
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +15 -7
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +2 -2
- data/lib/rubocop/cop/rails/inverse_of.rb +130 -8
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +3 -3
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +3 -2
- data/lib/rubocop/cop/rails/presence.rb +31 -18
- data/lib/rubocop/cop/rails/present.rb +11 -8
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +52 -10
- data/lib/rubocop/cop/rails/request_referer.rb +2 -3
- data/lib/rubocop/cop/style/auto_resource_cleanup.rb +9 -2
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +38 -10
- data/lib/rubocop/cop/style/class_and_module_children.rb +76 -0
- data/lib/rubocop/cop/style/commented_keyword.rb +1 -1
- data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
- data/lib/rubocop/cop/style/format_string_token.rb +24 -4
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +46 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +4 -3
- data/lib/rubocop/cop/style/if_unless_modifier.rb +14 -0
- data/lib/rubocop/cop/style/method_def_parentheses.rb +79 -0
- data/lib/rubocop/cop/style/mixin_usage.rb +13 -2
- data/lib/rubocop/cop/style/multiline_if_modifier.rb +1 -1
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +19 -0
- data/lib/rubocop/cop/style/negated_if.rb +1 -1
- data/lib/rubocop/cop/style/negated_while.rb +6 -4
- data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +4 -0
- data/lib/rubocop/cop/style/redundant_conditional.rb +1 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
- data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +1 -1
- data/lib/rubocop/cop/style/string_literals.rb +4 -0
- data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +4 -0
- data/lib/rubocop/cop/style/trailing_body_on_method_definition.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -0
- data/lib/rubocop/cop/style/trailing_comma_in_literal.rb +4 -0
- data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +33 -24
- data/lib/rubocop/options.rb +33 -10
- data/lib/rubocop/path_util.rb +7 -0
- data/lib/rubocop/token.rb +4 -0
- data/lib/rubocop/version.rb +1 -1
- metadata +14 -4
- data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +0 -149
- data/lib/rubocop/cop/style/extend_self.rb +0 -92
@@ -38,9 +38,10 @@ module RuboCop
|
|
38
38
|
# something
|
39
39
|
# end
|
40
40
|
class Blank < Cop
|
41
|
-
MSG_NIL_OR_EMPTY = 'Use
|
42
|
-
MSG_NOT_PRESENT = 'Use
|
43
|
-
MSG_UNLESS_PRESENT = 'Use `if
|
41
|
+
MSG_NIL_OR_EMPTY = 'Use `%<prefer>s` instead of `%<current>s`.'.freeze
|
42
|
+
MSG_NOT_PRESENT = 'Use `%<prefer>s` instead of `%<current>s`.'.freeze
|
43
|
+
MSG_UNLESS_PRESENT = 'Use `if %<prefer>s` instead of ' \
|
44
|
+
'`%<current>s`.'.freeze
|
44
45
|
|
45
46
|
# `(send nil $_)` is not actually a valid match for an offense. Nodes
|
46
47
|
# that have a single method call on the left hand side
|
@@ -73,8 +74,8 @@ module RuboCop
|
|
73
74
|
not_present?(node) do |receiver|
|
74
75
|
add_offense(node,
|
75
76
|
message: format(MSG_NOT_PRESENT,
|
76
|
-
replacement(receiver),
|
77
|
-
node.source))
|
77
|
+
prefer: replacement(receiver),
|
78
|
+
current: node.source))
|
78
79
|
end
|
79
80
|
end
|
80
81
|
|
@@ -86,8 +87,8 @@ module RuboCop
|
|
86
87
|
|
87
88
|
add_offense(node,
|
88
89
|
message: format(MSG_NIL_OR_EMPTY,
|
89
|
-
replacement(variable1),
|
90
|
-
node.source))
|
90
|
+
prefer: replacement(variable1),
|
91
|
+
current: node.source))
|
91
92
|
end
|
92
93
|
end
|
93
94
|
|
@@ -101,8 +102,8 @@ module RuboCop
|
|
101
102
|
add_offense(node,
|
102
103
|
location: range,
|
103
104
|
message: format(MSG_UNLESS_PRESENT,
|
104
|
-
replacement(receiver),
|
105
|
-
range.source))
|
105
|
+
prefer: replacement(receiver),
|
106
|
+
current: range.source))
|
106
107
|
end
|
107
108
|
end
|
108
109
|
|
@@ -20,27 +20,37 @@ module RuboCop
|
|
20
20
|
# When EnforcedStyle is 'flexible' then only 'Date.today' is prohibited
|
21
21
|
# and only 'to_time' is reported as warning.
|
22
22
|
#
|
23
|
-
# @example
|
24
|
-
# #
|
25
|
-
# Time.zone.today
|
26
|
-
# Time.zone.today - 1.day
|
27
|
-
#
|
28
|
-
# # flexible
|
23
|
+
# @example EnforcedStyle: strict
|
24
|
+
# # bad
|
29
25
|
# Date.current
|
30
26
|
# Date.yesterday
|
27
|
+
# Date.today
|
28
|
+
# date.to_time
|
29
|
+
# date.to_time_in_current_zone
|
30
|
+
#
|
31
|
+
# # good
|
32
|
+
# Time.zone.today
|
33
|
+
# Time.zone.today - 1.day
|
31
34
|
#
|
32
|
-
#
|
35
|
+
# @example EnforcedStyle: flexible (default)
|
36
|
+
# # bad
|
33
37
|
# Date.today
|
34
38
|
# date.to_time
|
35
39
|
#
|
36
|
-
# #
|
40
|
+
# # good
|
41
|
+
# Time.zone.today
|
42
|
+
# Time.zone.today - 1.day
|
43
|
+
# Date.current
|
44
|
+
# Date.yesterday
|
37
45
|
# date.to_time_in_current_zone
|
46
|
+
#
|
38
47
|
class Date < Cop
|
39
48
|
include ConfigurableEnforcedStyle
|
40
49
|
|
41
|
-
MSG = 'Do not use
|
50
|
+
MSG = 'Do not use `Date.%<day>s` without zone. Use ' \
|
51
|
+
'`Time.zone.%<day>s` instead.'.freeze
|
42
52
|
|
43
|
-
MSG_SEND = 'Do not use
|
53
|
+
MSG_SEND = 'Do not use `%<method>s` on Date objects, because they ' \
|
44
54
|
'know nothing about the time zone in use.'.freeze
|
45
55
|
|
46
56
|
BAD_DAYS = %i[today current yesterday tomorrow].freeze
|
@@ -59,7 +69,7 @@ module RuboCop
|
|
59
69
|
return if safe_chain?(node) || safe_to_time?(node)
|
60
70
|
|
61
71
|
add_offense(node, location: :selector,
|
62
|
-
message: format(MSG_SEND, node.method_name))
|
72
|
+
message: format(MSG_SEND, method: node.method_name))
|
63
73
|
end
|
64
74
|
|
65
75
|
private
|
@@ -72,9 +82,7 @@ module RuboCop
|
|
72
82
|
method_name = (chain & bad_days).join('.')
|
73
83
|
|
74
84
|
add_offense(node, location: :selector,
|
75
|
-
message: format(MSG,
|
76
|
-
"Date.#{method_name}",
|
77
|
-
"Time.zone.#{method_name}"))
|
85
|
+
message: format(MSG, day: method_name.to_s))
|
78
86
|
end
|
79
87
|
|
80
88
|
def extract_method_chain(node)
|
@@ -26,7 +26,7 @@ module RuboCop
|
|
26
26
|
# # good
|
27
27
|
# User.find_by!(email: email)
|
28
28
|
class DynamicFindBy < Cop
|
29
|
-
MSG = 'Use
|
29
|
+
MSG = 'Use `%<static_name>s` instead of dynamic `%<method>s`.'.freeze
|
30
30
|
METHOD_PATTERN = /^find_by_(.+?)(!)?$/
|
31
31
|
|
32
32
|
def on_send(node)
|
@@ -39,7 +39,8 @@ module RuboCop
|
|
39
39
|
return unless static_name
|
40
40
|
|
41
41
|
add_offense(node,
|
42
|
-
message: format(MSG, static_name,
|
42
|
+
message: format(MSG, static_name: static_name,
|
43
|
+
method: node.method_name))
|
43
44
|
end
|
44
45
|
|
45
46
|
def autocorrect(node)
|
@@ -20,7 +20,8 @@ module RuboCop
|
|
20
20
|
class EnumUniqueness < Cop
|
21
21
|
include Duplication
|
22
22
|
|
23
|
-
MSG = 'Duplicate value
|
23
|
+
MSG = 'Duplicate value `%<value>s` found in `%<enum>s` ' \
|
24
|
+
'enum declaration.'.freeze
|
24
25
|
|
25
26
|
def_node_matcher :enum_declaration, <<-PATTERN
|
26
27
|
(send nil? :enum (hash (pair (_ $_) ${array hash})))
|
@@ -33,7 +34,8 @@ module RuboCop
|
|
33
34
|
return unless duplicates?(items)
|
34
35
|
|
35
36
|
consecutive_duplicates(items).each do |item|
|
36
|
-
add_offense(item, message: format(MSG, item.source,
|
37
|
+
add_offense(item, message: format(MSG, value: item.source,
|
38
|
+
enum: name))
|
37
39
|
end
|
38
40
|
end
|
39
41
|
end
|
@@ -16,7 +16,7 @@ module RuboCop
|
|
16
16
|
# # good
|
17
17
|
# Rails.env.production?
|
18
18
|
class EnvironmentComparison < Cop
|
19
|
-
MSG =
|
19
|
+
MSG = "Favor `Rails.env.%<env>s?` over `Rails.env == '%<env>s'`.".freeze
|
20
20
|
|
21
21
|
SYM_MSG = 'Do not compare `Rails.env` with a symbol, it will always ' \
|
22
22
|
'evaluate to `false`.'.freeze
|
@@ -40,7 +40,7 @@ module RuboCop
|
|
40
40
|
def on_send(node)
|
41
41
|
environment_str_comparison?(node) do |env_node|
|
42
42
|
env, = *env_node
|
43
|
-
add_offense(node, message: format(MSG, env
|
43
|
+
add_offense(node, message: format(MSG, env: env))
|
44
44
|
end
|
45
45
|
environment_sym_comparison?(node) do |_|
|
46
46
|
add_offense(node, message: SYM_MSG)
|
@@ -62,7 +62,7 @@ module RuboCop
|
|
62
62
|
|
63
63
|
def register_offense(node)
|
64
64
|
line_range = node.loc.column...node.loc.last_column
|
65
|
-
source_range = source_range(processed_source.buffer, node.
|
65
|
+
source_range = source_range(processed_source.buffer, node.first_line,
|
66
66
|
line_range)
|
67
67
|
add_offense(node, location: source_range)
|
68
68
|
end
|
@@ -14,7 +14,7 @@ module RuboCop
|
|
14
14
|
# # good
|
15
15
|
# User.find_by(name: 'Bruce')
|
16
16
|
class FindBy < Cop
|
17
|
-
MSG = 'Use `find_by` instead of `where
|
17
|
+
MSG = 'Use `find_by` instead of `where.%<method>s`.'.freeze
|
18
18
|
TARGET_SELECTORS = %i[first take].freeze
|
19
19
|
|
20
20
|
def_node_matcher :where_first?, <<-PATTERN
|
@@ -28,7 +28,7 @@ module RuboCop
|
|
28
28
|
node.loc.selector.end_pos)
|
29
29
|
|
30
30
|
add_offense(node, location: range,
|
31
|
-
message: format(MSG, node.method_name))
|
31
|
+
message: format(MSG, method: node.method_name))
|
32
32
|
end
|
33
33
|
|
34
34
|
def autocorrect(node)
|
@@ -47,21 +47,29 @@ module RuboCop
|
|
47
47
|
PATTERN
|
48
48
|
|
49
49
|
def on_send(node)
|
50
|
-
|
50
|
+
unless association_without_options?(node)
|
51
51
|
return if valid_options?(association_with_options?(node))
|
52
|
-
else
|
53
|
-
n = node.parent.begin_type? ? node.parent.parent : node.parent
|
54
|
-
|
55
|
-
if with_options_block(n)
|
56
|
-
return if valid_options?(with_options_block(n))
|
57
|
-
end
|
58
52
|
end
|
59
53
|
|
54
|
+
return if valid_options_in_with_options_block?(node)
|
55
|
+
|
60
56
|
add_offense(node, location: :selector)
|
61
57
|
end
|
62
58
|
|
63
59
|
private
|
64
60
|
|
61
|
+
def valid_options_in_with_options_block?(node)
|
62
|
+
return true unless node.parent
|
63
|
+
|
64
|
+
n = node.parent.begin_type? ? node.parent.parent : node.parent
|
65
|
+
|
66
|
+
if with_options_block(n)
|
67
|
+
return true if valid_options?(with_options_block(n))
|
68
|
+
end
|
69
|
+
|
70
|
+
false
|
71
|
+
end
|
72
|
+
|
65
73
|
def valid_options?(options)
|
66
74
|
return true unless options
|
67
75
|
return true if options.any? do |o|
|
@@ -20,7 +20,7 @@ module RuboCop
|
|
20
20
|
extend TargetRailsVersion
|
21
21
|
|
22
22
|
MSG = 'Use keyword arguments instead of ' \
|
23
|
-
'positional arguments for http call:
|
23
|
+
'positional arguments for http call: `%<verb>s`.'.freeze
|
24
24
|
KEYWORD_ARGS = %i[
|
25
25
|
headers env params body flash as xhr session method
|
26
26
|
].freeze
|
@@ -37,7 +37,7 @@ module RuboCop
|
|
37
37
|
return unless needs_conversion?(data)
|
38
38
|
|
39
39
|
add_offense(node, location: :selector,
|
40
|
-
message: format(MSG, node.method_name))
|
40
|
+
message: format(MSG, verb: node.method_name))
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -10,19 +10,109 @@ module RuboCop
|
|
10
10
|
# for associations to work in both ways, or set to `false` to opt-out.
|
11
11
|
#
|
12
12
|
# @example
|
13
|
+
# # good
|
14
|
+
# class Blog < ApplicationRecord
|
15
|
+
# has_many :posts
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# class Post < ApplicationRecord
|
19
|
+
# belongs_to :blog
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# @example
|
13
23
|
# # bad
|
14
24
|
# class Blog < ApplicationRecord
|
15
|
-
# has_many :
|
25
|
+
# has_many :posts, -> { order(published_at: :desc) }
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# class Post < ApplicationRecord
|
29
|
+
# belongs_to :blog
|
16
30
|
# end
|
17
31
|
#
|
18
32
|
# # good
|
19
33
|
# class Blog < ApplicationRecord
|
20
|
-
# has_many(:
|
34
|
+
# has_many(:posts,
|
21
35
|
# -> { order(published_at: :desc) },
|
22
36
|
# inverse_of: :blog
|
23
37
|
# )
|
24
38
|
# end
|
25
39
|
#
|
40
|
+
# class Post < ApplicationRecord
|
41
|
+
# belongs_to :blog
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# # good
|
45
|
+
# class Blog < ApplicationRecord
|
46
|
+
# with_options inverse_of: :blog do
|
47
|
+
# has_many :posts, -> { order(published_at: :desc) }
|
48
|
+
# end
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# class Post < ApplicationRecord
|
52
|
+
# belongs_to :blog
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# @example
|
56
|
+
# # bad
|
57
|
+
# class Picture < ApplicationRecord
|
58
|
+
# belongs_to :imageable, polymorphic: true
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# class Employee < ApplicationRecord
|
62
|
+
# has_many :pictures, as: :imageable
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# class Product < ApplicationRecord
|
66
|
+
# has_many :pictures, as: :imageable
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# # good
|
70
|
+
# class Picture < ApplicationRecord
|
71
|
+
# belongs_to :imageable, polymorphic: true
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# class Employee < ApplicationRecord
|
75
|
+
# has_many :pictures, as: :imageable, inverse_of: :imageable
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# class Product < ApplicationRecord
|
79
|
+
# has_many :pictures, as: :imageable, inverse_of: :imageable
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# @example
|
83
|
+
# # bad
|
84
|
+
# # However, RuboCop can not detect this pattern...
|
85
|
+
# class Physician < ApplicationRecord
|
86
|
+
# has_many :appointments
|
87
|
+
# has_many :patients, through: :appointments
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# class Appointment < ApplicationRecord
|
91
|
+
# belongs_to :physician
|
92
|
+
# belongs_to :patient
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# class Patient < ApplicationRecord
|
96
|
+
# has_many :appointments
|
97
|
+
# has_many :physicians, through: :appointments
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# # good
|
101
|
+
# class Physician < ApplicationRecord
|
102
|
+
# has_many :appointments
|
103
|
+
# has_many :patients, through: :appointments
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# class Appointment < ApplicationRecord
|
107
|
+
# belongs_to :physician, inverse_of: :appointments
|
108
|
+
# belongs_to :patient, inverse_of: :appointments
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# class Patient < ApplicationRecord
|
112
|
+
# has_many :appointments
|
113
|
+
# has_many :physicians, through: :appointments
|
114
|
+
# end
|
115
|
+
#
|
26
116
|
# @see http://guides.rubyonrails.org/association_basics.html#bi-directional-associations
|
27
117
|
# @see http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Setting+Inverses
|
28
118
|
class InverseOf < Cop
|
@@ -32,8 +122,8 @@ module RuboCop
|
|
32
122
|
|
33
123
|
MSG = 'Specify an `:inverse_of` option.'.freeze
|
34
124
|
|
35
|
-
def_node_matcher :
|
36
|
-
(send
|
125
|
+
def_node_matcher :association_recv_arguments, <<-PATTERN
|
126
|
+
(send $_ {:has_many :has_one :belongs_to} _ $...)
|
37
127
|
PATTERN
|
38
128
|
|
39
129
|
def_node_matcher :options_from_argument, <<-PATTERN
|
@@ -52,6 +142,14 @@ module RuboCop
|
|
52
142
|
(pair (sym :polymorphic) !nil)
|
53
143
|
PATTERN
|
54
144
|
|
145
|
+
def_node_matcher :as_option?, <<-PATTERN
|
146
|
+
(pair (sym :as) !nil)
|
147
|
+
PATTERN
|
148
|
+
|
149
|
+
def_node_matcher :class_name_option?, <<-PATTERN
|
150
|
+
(pair (sym :class_name) !nil)
|
151
|
+
PATTERN
|
152
|
+
|
55
153
|
def_node_matcher :foreign_key_option?, <<-PATTERN
|
56
154
|
(pair (sym :foreign_key) !nil)
|
57
155
|
PATTERN
|
@@ -61,10 +159,15 @@ module RuboCop
|
|
61
159
|
PATTERN
|
62
160
|
|
63
161
|
def on_send(node)
|
64
|
-
arguments =
|
162
|
+
recv, arguments = association_recv_arguments(node)
|
65
163
|
return unless arguments
|
164
|
+
with_options = with_options_arguments(recv, node)
|
165
|
+
|
166
|
+
options = arguments.concat(with_options).flat_map do |arg|
|
167
|
+
options_from_argument(arg)
|
168
|
+
end
|
169
|
+
return if options_ignoring_inverse_of?(options)
|
66
170
|
|
67
|
-
options = arguments.flat_map { |arg| options_from_argument(arg) }
|
68
171
|
return unless scope?(arguments) ||
|
69
172
|
options_requiring_inverse_of?(options)
|
70
173
|
|
@@ -79,17 +182,36 @@ module RuboCop
|
|
79
182
|
def options_requiring_inverse_of?(options)
|
80
183
|
required = options.any? do |opt|
|
81
184
|
conditions_option?(opt) ||
|
82
|
-
|
185
|
+
class_name_option?(opt) ||
|
83
186
|
foreign_key_option?(opt)
|
84
187
|
end
|
85
188
|
|
86
189
|
return required if target_rails_version >= 5.2
|
87
|
-
required || options.any? { |opt|
|
190
|
+
required || options.any? { |opt| as_option?(opt) }
|
191
|
+
end
|
192
|
+
|
193
|
+
def options_ignoring_inverse_of?(options)
|
194
|
+
options.any? do |opt|
|
195
|
+
through_option?(opt) || polymorphic_option?(opt)
|
196
|
+
end
|
88
197
|
end
|
89
198
|
|
90
199
|
def options_contain_inverse_of?(options)
|
91
200
|
options.any? { |opt| inverse_of_option?(opt) }
|
92
201
|
end
|
202
|
+
|
203
|
+
def with_options_arguments(recv, node)
|
204
|
+
blocks = node.each_ancestor(:block).select do |block|
|
205
|
+
block.send_node.command?(:with_options) &&
|
206
|
+
same_context_in_with_options?(block.arguments.first, recv)
|
207
|
+
end
|
208
|
+
blocks.flat_map { |n| n.send_node.arguments }
|
209
|
+
end
|
210
|
+
|
211
|
+
def same_context_in_with_options?(arg, recv)
|
212
|
+
return true if arg.nil? && recv.nil?
|
213
|
+
arg && recv && arg.children[0] == recv.children[0]
|
214
|
+
end
|
93
215
|
end
|
94
216
|
end
|
95
217
|
end
|