rubocop 0.52.0 → 0.52.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -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
|