rubocop-rspec 2.16.0 → 2.24.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/CHANGELOG.md +124 -9
- data/README.md +3 -3
- data/config/default.yml +145 -18
- data/config/obsoletion.yml +15 -0
- data/lib/rubocop/cop/rspec/be_empty.rb +44 -0
- data/lib/rubocop/cop/rspec/be_nil.rb +2 -2
- data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +29 -115
- data/lib/rubocop/cop/rspec/capybara/match_style.rb +38 -0
- data/lib/rubocop/cop/rspec/capybara/negation_matcher.rb +23 -96
- data/lib/rubocop/cop/rspec/capybara/specific_actions.rb +19 -75
- data/lib/rubocop/cop/rspec/capybara/specific_finders.rb +14 -83
- data/lib/rubocop/cop/rspec/capybara/specific_matcher.rb +25 -69
- data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +26 -63
- data/lib/rubocop/cop/rspec/change_by_zero.rb +33 -23
- data/lib/rubocop/cop/rspec/contain_exactly.rb +56 -0
- data/lib/rubocop/cop/rspec/context_method.rb +5 -1
- data/lib/rubocop/cop/rspec/context_wording.rb +13 -6
- data/lib/rubocop/cop/rspec/describe_method.rb +16 -8
- data/lib/rubocop/cop/rspec/described_class.rb +2 -1
- data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +7 -5
- data/lib/rubocop/cop/rspec/dialect.rb +1 -1
- data/lib/rubocop/cop/rspec/duplicated_metadata.rb +2 -2
- data/lib/rubocop/cop/rspec/empty_example_group.rb +10 -7
- data/lib/rubocop/cop/rspec/empty_hook.rb +2 -2
- data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_metadata.rb +46 -0
- data/lib/rubocop/cop/rspec/eq.rb +47 -0
- data/lib/rubocop/cop/rspec/example_wording.rb +1 -1
- data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +14 -5
- data/lib/rubocop/cop/rspec/expect_actual.rb +4 -4
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -1
- data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +25 -118
- data/lib/rubocop/cop/rspec/factory_bot/consistent_parentheses_style.rb +40 -107
- data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +30 -250
- data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +19 -46
- data/lib/rubocop/cop/rspec/factory_bot/factory_name_style.rb +23 -64
- data/lib/rubocop/cop/rspec/factory_bot/syntax_methods.rb +45 -79
- data/lib/rubocop/cop/rspec/file_path.rb +8 -2
- data/lib/rubocop/cop/rspec/focus.rb +19 -5
- data/lib/rubocop/cop/rspec/hook_argument.rb +12 -9
- data/lib/rubocop/cop/rspec/hooks_before_examples.rb +5 -3
- data/lib/rubocop/cop/rspec/indexed_let.rb +112 -0
- data/lib/rubocop/cop/rspec/instance_variable.rb +1 -1
- data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -1
- data/lib/rubocop/cop/rspec/let_before_examples.rb +8 -4
- data/lib/rubocop/cop/rspec/let_setup.rb +6 -8
- data/lib/rubocop/cop/rspec/match_array.rb +59 -0
- data/lib/rubocop/cop/rspec/metadata_style.rb +197 -0
- data/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb +1 -2
- data/lib/rubocop/cop/rspec/mixin/file_help.rb +14 -0
- data/lib/rubocop/cop/rspec/mixin/location_help.rb +37 -0
- data/lib/rubocop/cop/rspec/mixin/metadata.rb +21 -7
- data/lib/rubocop/cop/rspec/mixin/skip_or_pending.rb +20 -4
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +2 -1
- data/lib/rubocop/cop/rspec/named_subject.rb +7 -5
- data/lib/rubocop/cop/rspec/no_expectation_example.rb +2 -5
- data/lib/rubocop/cop/rspec/overwriting_setup.rb +3 -1
- data/lib/rubocop/cop/rspec/pending.rb +23 -13
- data/lib/rubocop/cop/rspec/pending_without_reason.rb +72 -36
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +49 -40
- data/lib/rubocop/cop/rspec/rails/have_http_status.rb +11 -6
- data/lib/rubocop/cop/rspec/rails/http_status.rb +107 -34
- data/lib/rubocop/cop/rspec/rails/inferred_spec_type.rb +4 -4
- data/lib/rubocop/cop/rspec/rails/minitest_assertions.rb +60 -0
- data/lib/rubocop/cop/rspec/rails/negation_be_valid.rb +102 -0
- data/lib/rubocop/cop/rspec/rails/travel_around.rb +92 -0
- data/lib/rubocop/cop/rspec/receive_counts.rb +1 -1
- data/lib/rubocop/cop/rspec/receive_messages.rb +161 -0
- data/lib/rubocop/cop/rspec/redundant_around.rb +65 -0
- data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +3 -6
- data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +3 -6
- data/lib/rubocop/cop/rspec/repeated_include_example.rb +3 -4
- data/lib/rubocop/cop/rspec/scattered_setup.rb +23 -6
- data/lib/rubocop/cop/rspec/shared_context.rb +12 -13
- data/lib/rubocop/cop/rspec/shared_examples.rb +6 -4
- data/lib/rubocop/cop/rspec/skip_block_inside_example.rb +46 -0
- data/lib/rubocop/cop/rspec/sort_metadata.rb +4 -3
- data/lib/rubocop/cop/rspec/spec_file_path_format.rb +133 -0
- data/lib/rubocop/cop/rspec/spec_file_path_suffix.rb +40 -0
- data/lib/rubocop/cop/rspec/stubbed_mock.rb +1 -1
- data/lib/rubocop/cop/rspec/subject_stub.rb +0 -1
- data/lib/rubocop/cop/rspec/variable_definition.rb +5 -2
- data/lib/rubocop/cop/rspec/variable_name.rb +4 -1
- data/lib/rubocop/cop/rspec/verified_double_reference.rb +7 -7
- data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -1
- data/lib/rubocop/cop/rspec/void_expect.rb +2 -1
- data/lib/rubocop/cop/rspec_cops.rb +16 -0
- data/lib/rubocop/rspec/config_formatter.rb +16 -0
- data/lib/rubocop/rspec/example_group.rb +6 -8
- data/lib/rubocop/rspec/language/node_pattern.rb +26 -0
- data/lib/rubocop/rspec/language.rb +25 -16
- data/lib/rubocop/rspec/version.rb +1 -1
- data/lib/rubocop-rspec.rb +4 -5
- metadata +50 -8
- data/lib/rubocop/cop/rspec/mixin/capybara_help.rb +0 -80
- data/lib/rubocop/cop/rspec/mixin/css_selector.rb +0 -146
- data/lib/rubocop/rspec/factory_bot/language.rb +0 -37
- data/lib/rubocop/rspec/factory_bot.rb +0 -64
@@ -24,7 +24,7 @@ module RuboCop
|
|
24
24
|
class ExpectActual < Base
|
25
25
|
extend AutoCorrector
|
26
26
|
|
27
|
-
MSG = 'Provide the actual you are testing to `expect(...)`.'
|
27
|
+
MSG = 'Provide the actual value you are testing to `expect(...)`.'
|
28
28
|
|
29
29
|
RESTRICT_ON_SEND = Runners.all
|
30
30
|
|
@@ -77,7 +77,7 @@ module RuboCop
|
|
77
77
|
|
78
78
|
private
|
79
79
|
|
80
|
-
# This is not
|
80
|
+
# This is not implemented using a NodePattern because it seems
|
81
81
|
# to not be able to match against an explicit (nil) sexp
|
82
82
|
def literal?(node)
|
83
83
|
node && (simple_literal?(node) || complex_literal?(node))
|
@@ -93,8 +93,8 @@ module RuboCop
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def swap(corrector, actual, expected)
|
96
|
-
corrector.replace(actual
|
97
|
-
corrector.replace(expected
|
96
|
+
corrector.replace(actual, expected.source)
|
97
|
+
corrector.replace(expected, actual.source)
|
98
98
|
end
|
99
99
|
end
|
100
100
|
end
|
@@ -25,7 +25,7 @@ module RuboCop
|
|
25
25
|
MSG = 'Do not use `%<expect>s` in `%<hook>s` hook'
|
26
26
|
|
27
27
|
# @!method expectation(node)
|
28
|
-
def_node_search :expectation,
|
28
|
+
def_node_search :expectation, '(send nil? #Expectations.all ...)'
|
29
29
|
|
30
30
|
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
31
31
|
return unless hook?(node)
|
@@ -4,124 +4,31 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
6
|
module FactoryBot
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# #
|
11
|
-
#
|
12
|
-
#
|
13
|
-
# #
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# #
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# #
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# #
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# #
|
26
|
-
#
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
# @!method value_matcher(node)
|
34
|
-
def_node_matcher :value_matcher, <<-PATTERN
|
35
|
-
(send _ !#reserved_method? $...)
|
36
|
-
PATTERN
|
37
|
-
|
38
|
-
# @!method factory_attributes(node)
|
39
|
-
def_node_matcher :factory_attributes, <<-PATTERN
|
40
|
-
(block (send _ #attribute_defining_method? ...) _ { (begin $...) $(send ...) } )
|
41
|
-
PATTERN
|
42
|
-
|
43
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
44
|
-
attributes = factory_attributes(node) || []
|
45
|
-
attributes = [attributes] unless attributes.is_a?(Array) # rubocop:disable Style/ArrayCoercion, Lint/RedundantCopDisableDirective
|
46
|
-
|
47
|
-
attributes.each do |attribute|
|
48
|
-
next unless offensive_receiver?(attribute.receiver, node)
|
49
|
-
next if proc?(attribute) || association?(attribute.first_argument)
|
50
|
-
|
51
|
-
add_offense(attribute) do |corrector|
|
52
|
-
autocorrect(corrector, attribute)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
def autocorrect(corrector, node)
|
60
|
-
if node.parenthesized?
|
61
|
-
autocorrect_replacing_parens(corrector, node)
|
62
|
-
else
|
63
|
-
autocorrect_without_parens(corrector, node)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def offensive_receiver?(receiver, node)
|
68
|
-
receiver.nil? ||
|
69
|
-
receiver.self_type? ||
|
70
|
-
receiver_matches_first_block_argument?(receiver, node)
|
71
|
-
end
|
72
|
-
|
73
|
-
def receiver_matches_first_block_argument?(receiver, node)
|
74
|
-
first_block_argument = node.arguments.first
|
75
|
-
|
76
|
-
!first_block_argument.nil? &&
|
77
|
-
receiver.lvar_type? &&
|
78
|
-
receiver.node_parts == first_block_argument.node_parts
|
79
|
-
end
|
80
|
-
|
81
|
-
def proc?(attribute)
|
82
|
-
value_matcher(attribute).to_a.all?(&:block_pass_type?)
|
83
|
-
end
|
84
|
-
|
85
|
-
# @!method association?(node)
|
86
|
-
def_node_matcher :association?, '(hash <(pair (sym :factory) _) ...>)'
|
87
|
-
|
88
|
-
def autocorrect_replacing_parens(corrector, node)
|
89
|
-
left_braces, right_braces = braces(node)
|
90
|
-
|
91
|
-
corrector.replace(node.location.begin, " #{left_braces}")
|
92
|
-
corrector.replace(node.location.end, right_braces)
|
93
|
-
end
|
94
|
-
|
95
|
-
def autocorrect_without_parens(corrector, node)
|
96
|
-
left_braces, right_braces = braces(node)
|
97
|
-
|
98
|
-
argument = node.first_argument
|
99
|
-
expression = argument.location.expression
|
100
|
-
corrector.insert_before(expression, left_braces)
|
101
|
-
corrector.insert_after(expression, right_braces)
|
102
|
-
end
|
103
|
-
|
104
|
-
def braces(node)
|
105
|
-
if value_hash_without_braces?(node.first_argument)
|
106
|
-
['{ { ', ' } }']
|
107
|
-
else
|
108
|
-
['{ ', ' }']
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
def value_hash_without_braces?(node)
|
113
|
-
node.hash_type? && !node.braces?
|
114
|
-
end
|
115
|
-
|
116
|
-
def reserved_method?(method_name)
|
117
|
-
RuboCop::RSpec::FactoryBot.reserved_methods.include?(method_name)
|
118
|
-
end
|
119
|
-
|
120
|
-
def attribute_defining_method?(method_name)
|
121
|
-
RuboCop::RSpec::FactoryBot.attribute_defining_methods
|
122
|
-
.include?(method_name)
|
123
|
-
end
|
124
|
-
end
|
7
|
+
# @!parse
|
8
|
+
# # Always declare attribute values as blocks.
|
9
|
+
# #
|
10
|
+
# # @example
|
11
|
+
# # # bad
|
12
|
+
# # kind [:active, :rejected].sample
|
13
|
+
# #
|
14
|
+
# # # good
|
15
|
+
# # kind { [:active, :rejected].sample }
|
16
|
+
# #
|
17
|
+
# # # bad
|
18
|
+
# # closed_at 1.day.from_now
|
19
|
+
# #
|
20
|
+
# # # good
|
21
|
+
# # closed_at { 1.day.from_now }
|
22
|
+
# #
|
23
|
+
# # # bad
|
24
|
+
# # count 1
|
25
|
+
# #
|
26
|
+
# # # good
|
27
|
+
# # count { 1 }
|
28
|
+
# #
|
29
|
+
# class AttributeDefinedStatically < ::RuboCop::Cop::Base; end
|
30
|
+
AttributeDefinedStatically =
|
31
|
+
::RuboCop::Cop::FactoryBot::AttributeDefinedStatically
|
125
32
|
end
|
126
33
|
end
|
127
34
|
end
|
@@ -4,113 +4,46 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
6
|
module FactoryBot
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# #
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# create
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# #
|
20
|
-
#
|
21
|
-
# create(:user)
|
22
|
-
# create(:
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# #
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
# create :login
|
32
|
-
#
|
33
|
-
# #
|
34
|
-
# #
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
include RuboCop::Cop::Util
|
48
|
-
|
49
|
-
def self.autocorrect_incompatible_with
|
50
|
-
[Style::MethodCallWithArgsParentheses]
|
51
|
-
end
|
52
|
-
|
53
|
-
MSG_REQUIRE_PARENS = 'Prefer method call with parentheses'
|
54
|
-
MSG_OMIT_PARENS = 'Prefer method call without parentheses'
|
55
|
-
|
56
|
-
FACTORY_CALLS = RuboCop::RSpec::FactoryBot::Language::METHODS
|
57
|
-
|
58
|
-
RESTRICT_ON_SEND = FACTORY_CALLS
|
59
|
-
|
60
|
-
# @!method factory_call(node)
|
61
|
-
def_node_matcher :factory_call, <<-PATTERN
|
62
|
-
(send
|
63
|
-
{#factory_bot? nil?} %FACTORY_CALLS
|
64
|
-
{sym str send lvar} _*
|
65
|
-
)
|
66
|
-
PATTERN
|
67
|
-
|
68
|
-
def on_send(node)
|
69
|
-
return if ambiguous_without_parentheses?(node)
|
70
|
-
|
71
|
-
factory_call(node) do
|
72
|
-
return if node.method?(:generate) && node.arguments.count > 1
|
73
|
-
|
74
|
-
if node.parenthesized?
|
75
|
-
process_with_parentheses(node)
|
76
|
-
else
|
77
|
-
process_without_parentheses(node)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
private
|
83
|
-
|
84
|
-
def process_with_parentheses(node)
|
85
|
-
return unless style == :omit_parentheses
|
86
|
-
return unless same_line?(node, node.first_argument)
|
87
|
-
|
88
|
-
add_offense(node.loc.selector,
|
89
|
-
message: MSG_OMIT_PARENS) do |corrector|
|
90
|
-
remove_parentheses(corrector, node)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def process_without_parentheses(node)
|
95
|
-
return unless style == :require_parentheses
|
96
|
-
|
97
|
-
add_offense(node.loc.selector,
|
98
|
-
message: MSG_REQUIRE_PARENS) do |corrector|
|
99
|
-
add_parentheses(node, corrector)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def ambiguous_without_parentheses?(node)
|
104
|
-
node.parent&.send_type? ||
|
105
|
-
node.parent&.pair_type? ||
|
106
|
-
node.parent&.array_type?
|
107
|
-
end
|
108
|
-
|
109
|
-
def remove_parentheses(corrector, node)
|
110
|
-
corrector.replace(node.location.begin, ' ')
|
111
|
-
corrector.remove(node.location.end)
|
112
|
-
end
|
113
|
-
end
|
7
|
+
# @!parse
|
8
|
+
# # Use a consistent style for parentheses in factory bot calls.
|
9
|
+
# #
|
10
|
+
# # @example
|
11
|
+
# #
|
12
|
+
# # # bad
|
13
|
+
# # create :user
|
14
|
+
# # build(:user)
|
15
|
+
# # create(:login)
|
16
|
+
# # create :login
|
17
|
+
# #
|
18
|
+
# # @example `EnforcedStyle: require_parentheses` (default)
|
19
|
+
# #
|
20
|
+
# # # good
|
21
|
+
# # create(:user)
|
22
|
+
# # create(:user)
|
23
|
+
# # create(:login)
|
24
|
+
# # build(:login)
|
25
|
+
# #
|
26
|
+
# # @example `EnforcedStyle: omit_parentheses`
|
27
|
+
# #
|
28
|
+
# # # good
|
29
|
+
# # create :user
|
30
|
+
# # build :user
|
31
|
+
# # create :login
|
32
|
+
# # create :login
|
33
|
+
# #
|
34
|
+
# # # also good
|
35
|
+
# # # when method name and first argument are not on same line
|
36
|
+
# # create(
|
37
|
+
# # :user
|
38
|
+
# # )
|
39
|
+
# # build(
|
40
|
+
# # :user,
|
41
|
+
# # name: 'foo'
|
42
|
+
# # )
|
43
|
+
# #
|
44
|
+
# class ConsistentParenthesesStyle < ::RuboCop::Cop::Base; end
|
45
|
+
ConsistentParenthesesStyle =
|
46
|
+
::RuboCop::Cop::FactoryBot::ConsistentParenthesesStyle
|
114
47
|
end
|
115
48
|
end
|
116
49
|
end
|
@@ -4,256 +4,36 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
6
|
module FactoryBot
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# #
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# #
|
16
|
-
#
|
17
|
-
#
|
18
|
-
# #
|
19
|
-
#
|
20
|
-
#
|
21
|
-
# #
|
22
|
-
#
|
23
|
-
#
|
24
|
-
# #
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
# #
|
29
|
-
#
|
30
|
-
#
|
31
|
-
# #
|
32
|
-
#
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
include RuboCop::RSpec::FactoryBot::Language
|
38
|
-
|
39
|
-
MSG_CREATE_LIST = 'Prefer create_list.'
|
40
|
-
MSG_N_TIMES = 'Prefer %<number>s.times.'
|
41
|
-
RESTRICT_ON_SEND = %i[create_list].freeze
|
42
|
-
|
43
|
-
# @!method array_new_or_n_times_block?(node)
|
44
|
-
def_node_matcher :array_new_or_n_times_block?, <<-PATTERN
|
45
|
-
(block
|
46
|
-
{
|
47
|
-
(send (const {nil? | cbase} :Array) :new (int _)) |
|
48
|
-
(send (int _) :times)
|
49
|
-
}
|
50
|
-
...
|
51
|
-
)
|
52
|
-
PATTERN
|
53
|
-
|
54
|
-
# @!method block_with_arg_and_used?(node)
|
55
|
-
def_node_matcher :block_with_arg_and_used?, <<-PATTERN
|
56
|
-
(block
|
57
|
-
_
|
58
|
-
(args (arg _value))
|
59
|
-
`_value
|
60
|
-
)
|
61
|
-
PATTERN
|
62
|
-
|
63
|
-
# @!method arguments_include_method_call?(node)
|
64
|
-
def_node_matcher :arguments_include_method_call?, <<-PATTERN
|
65
|
-
(send ${nil? #factory_bot?} :create (sym $_) `$(send ...))
|
66
|
-
PATTERN
|
67
|
-
|
68
|
-
# @!method factory_call(node)
|
69
|
-
def_node_matcher :factory_call, <<-PATTERN
|
70
|
-
(send ${nil? #factory_bot?} :create (sym $_) $...)
|
71
|
-
PATTERN
|
72
|
-
|
73
|
-
# @!method factory_list_call(node)
|
74
|
-
def_node_matcher :factory_list_call, <<-PATTERN
|
75
|
-
(send {nil? #factory_bot?} :create_list (sym _) (int $_) ...)
|
76
|
-
PATTERN
|
77
|
-
|
78
|
-
def on_block(node) # rubocop:todo InternalAffairs/NumblockHandler
|
79
|
-
return unless style == :create_list
|
80
|
-
|
81
|
-
return unless array_new_or_n_times_block?(node)
|
82
|
-
return if block_with_arg_and_used?(node)
|
83
|
-
return unless node.body
|
84
|
-
return if arguments_include_method_call?(node.body)
|
85
|
-
return unless contains_only_factory?(node.body)
|
86
|
-
|
87
|
-
add_offense(node.send_node, message: MSG_CREATE_LIST) do |corrector|
|
88
|
-
CreateListCorrector.new(node.send_node).call(corrector)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def on_send(node)
|
93
|
-
return unless style == :n_times
|
94
|
-
|
95
|
-
factory_list_call(node) do |count|
|
96
|
-
message = format(MSG_N_TIMES, number: count)
|
97
|
-
add_offense(node.loc.selector, message: message) do |corrector|
|
98
|
-
TimesCorrector.new(node).call(corrector)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
private
|
104
|
-
|
105
|
-
def contains_only_factory?(node)
|
106
|
-
if node.block_type?
|
107
|
-
factory_call(node.send_node)
|
108
|
-
else
|
109
|
-
factory_call(node)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
# :nodoc
|
114
|
-
module Corrector
|
115
|
-
private
|
116
|
-
|
117
|
-
def build_options_string(options)
|
118
|
-
options.map(&:source).join(', ')
|
119
|
-
end
|
120
|
-
|
121
|
-
def format_method_call(node, method, arguments)
|
122
|
-
if node.block_type? || node.parenthesized?
|
123
|
-
"#{method}(#{arguments})"
|
124
|
-
else
|
125
|
-
"#{method} #{arguments}"
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
def format_receiver(receiver)
|
130
|
-
return '' unless receiver
|
131
|
-
|
132
|
-
"#{receiver.source}."
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
# :nodoc
|
137
|
-
class TimesCorrector
|
138
|
-
include Corrector
|
139
|
-
|
140
|
-
def initialize(node)
|
141
|
-
@node = node
|
142
|
-
end
|
143
|
-
|
144
|
-
def call(corrector)
|
145
|
-
replacement = generate_n_times_block(node)
|
146
|
-
corrector.replace(node.block_node || node, replacement)
|
147
|
-
end
|
148
|
-
|
149
|
-
private
|
150
|
-
|
151
|
-
attr_reader :node
|
152
|
-
|
153
|
-
def generate_n_times_block(node)
|
154
|
-
factory, count, *options = node.arguments
|
155
|
-
|
156
|
-
arguments = factory.source
|
157
|
-
options = build_options_string(options)
|
158
|
-
arguments += ", #{options}" unless options.empty?
|
159
|
-
|
160
|
-
replacement = format_receiver(node.receiver)
|
161
|
-
replacement += format_method_call(node, 'create', arguments)
|
162
|
-
replacement += " #{factory_call_block_source}" if node.block_node
|
163
|
-
"#{count.source}.times { #{replacement} }"
|
164
|
-
end
|
165
|
-
|
166
|
-
def factory_call_block_source
|
167
|
-
node.block_node.location.begin.with(
|
168
|
-
end_pos: node.block_node.location.end.end_pos
|
169
|
-
).source
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
# :nodoc:
|
174
|
-
class CreateListCorrector
|
175
|
-
include Corrector
|
176
|
-
|
177
|
-
def initialize(node)
|
178
|
-
@node = node.parent
|
179
|
-
end
|
180
|
-
|
181
|
-
def call(corrector)
|
182
|
-
replacement = if node.body.block_type?
|
183
|
-
call_with_block_replacement(node)
|
184
|
-
else
|
185
|
-
call_replacement(node)
|
186
|
-
end
|
187
|
-
|
188
|
-
corrector.replace(node, replacement)
|
189
|
-
end
|
190
|
-
|
191
|
-
private
|
192
|
-
|
193
|
-
attr_reader :node
|
194
|
-
|
195
|
-
def call_with_block_replacement(node)
|
196
|
-
block = node.body
|
197
|
-
arguments = build_arguments(block, count_from(node))
|
198
|
-
replacement = format_receiver(block.receiver)
|
199
|
-
replacement += format_method_call(block, 'create_list', arguments)
|
200
|
-
replacement += format_block(block)
|
201
|
-
replacement
|
202
|
-
end
|
203
|
-
|
204
|
-
def build_arguments(node, count)
|
205
|
-
factory, *options = *node.send_node.arguments
|
206
|
-
|
207
|
-
arguments = ":#{factory.value}, #{count}"
|
208
|
-
options = build_options_string(options)
|
209
|
-
arguments += ", #{options}" unless options.empty?
|
210
|
-
arguments
|
211
|
-
end
|
212
|
-
|
213
|
-
def call_replacement(node)
|
214
|
-
block = node.body
|
215
|
-
factory, *options = *block.arguments
|
216
|
-
|
217
|
-
arguments = "#{factory.source}, #{count_from(node)}"
|
218
|
-
options = build_options_string(options)
|
219
|
-
arguments += ", #{options}" unless options.empty?
|
220
|
-
|
221
|
-
replacement = format_receiver(block.receiver)
|
222
|
-
replacement += format_method_call(block, 'create_list', arguments)
|
223
|
-
replacement
|
224
|
-
end
|
225
|
-
|
226
|
-
def count_from(node)
|
227
|
-
count_node =
|
228
|
-
if node.receiver.int_type?
|
229
|
-
node.receiver
|
230
|
-
else
|
231
|
-
node.send_node.first_argument
|
232
|
-
end
|
233
|
-
count_node.source
|
234
|
-
end
|
235
|
-
|
236
|
-
def format_block(node)
|
237
|
-
if node.body.begin_type?
|
238
|
-
format_multiline_block(node)
|
239
|
-
else
|
240
|
-
format_singleline_block(node)
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
def format_multiline_block(node)
|
245
|
-
indent = ' ' * node.body.loc.column
|
246
|
-
indent_end = ' ' * node.parent.loc.column
|
247
|
-
" do #{node.arguments.source}\n" \
|
248
|
-
"#{indent}#{node.body.source}\n" \
|
249
|
-
"#{indent_end}end"
|
250
|
-
end
|
251
|
-
|
252
|
-
def format_singleline_block(node)
|
253
|
-
" { #{node.arguments.source} #{node.body.source} }"
|
254
|
-
end
|
255
|
-
end
|
256
|
-
end
|
7
|
+
# @!parse
|
8
|
+
# # Checks for create_list usage.
|
9
|
+
# #
|
10
|
+
# # This cop can be configured using the `EnforcedStyle` option
|
11
|
+
# #
|
12
|
+
# # @example `EnforcedStyle: create_list` (default)
|
13
|
+
# # # bad
|
14
|
+
# # 3.times { create :user }
|
15
|
+
# #
|
16
|
+
# # # good
|
17
|
+
# # create_list :user, 3
|
18
|
+
# #
|
19
|
+
# # # bad
|
20
|
+
# # 3.times { create :user, age: 18 }
|
21
|
+
# #
|
22
|
+
# # # good - index is used to alter the created models attributes
|
23
|
+
# # 3.times { |n| create :user, age: n }
|
24
|
+
# #
|
25
|
+
# # # good - contains a method call, may return different values
|
26
|
+
# # 3.times { create :user, age: rand }
|
27
|
+
# #
|
28
|
+
# # @example `EnforcedStyle: n_times`
|
29
|
+
# # # bad
|
30
|
+
# # create_list :user, 3
|
31
|
+
# #
|
32
|
+
# # # good
|
33
|
+
# # 3.times { create :user }
|
34
|
+
# #
|
35
|
+
# class CreateList < ::RuboCop::Cop::Base; end
|
36
|
+
CreateList = ::RuboCop::Cop::FactoryBot::CreateList
|
257
37
|
end
|
258
38
|
end
|
259
39
|
end
|