Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 05d7bb0

Browse files
author
Maxim Krizhanovski
committed
Implement auto-correct for factory calls with block
1 parent d84e29c commit 05d7bb0

File tree

3 files changed

+136
-41
lines changed

3 files changed

+136
-41
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Master (Unreleased)
44

55
* Add `RSpec/Yield` cop, suggesting using the `and_yield` method when stubbing a method, accepting a block. ([@Darhazer][])
6+
* Fix `FactoryBot/CreateList` autocorrect crashing when the factory is called with a block=. ([@Darhazer][])
67

78
## 1.31.0 (2019-01-02)
89

lib/rubocop/cop/rspec/factory_bot/create_list.rb

+105-41
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ def on_send(node)
6969

7070
def autocorrect(node)
7171
if style == :create_list
72-
autocorrect_n_times_to_create_list(node)
72+
CreateListCorrector.new(node)
7373
else
74-
autocorrect_create_list_to_n_times(node)
74+
TimesCorrector.new(node)
7575
end
7676
end
7777

@@ -85,63 +85,127 @@ def contains_only_factory?(node)
8585
end
8686
end
8787

88-
def autocorrect_n_times_to_create_list(node)
89-
block = node.parent
90-
count = block.receiver.source
91-
replacement = factory_call_replacement(block.body, count)
88+
# :nodoc
89+
class Corrector
90+
private
9291

93-
lambda do |corrector|
94-
corrector.replace(block.loc.expression, replacement)
92+
def build_options_string(options)
93+
options.map(&:source).join(', ')
94+
end
95+
96+
def format_method_call(node, method, arguments)
97+
if node.block_type? || node.parenthesized?
98+
"#{method}(#{arguments})"
99+
else
100+
"#{method} #{arguments}"
101+
end
102+
end
103+
104+
def format_receiver(receiver)
105+
return '' unless receiver
106+
107+
"#{receiver.source}."
95108
end
96109
end
97110

98-
def autocorrect_create_list_to_n_times(node)
99-
replacement = generate_n_times_block(node)
100-
lambda do |corrector|
111+
# :nodoc
112+
class TimesCorrector < Corrector
113+
def initialize(node)
114+
@node = node
115+
end
116+
117+
def call(corrector)
118+
replacement = generate_n_times_block(node)
101119
corrector.replace(node.loc.expression, replacement)
102120
end
103-
end
104121

105-
def generate_n_times_block(node)
106-
receiver, factory, count, options = *factory_list_call(node)
122+
private
107123

108-
arguments = ":#{factory}"
109-
options = build_options_string(options)
110-
arguments += ", #{options}" unless options.empty?
124+
attr_reader :node
111125

112-
replacement = format_receiver(receiver)
113-
replacement += format_method_call(node, 'create', arguments)
114-
"#{count}.times { #{replacement} }"
126+
def generate_n_times_block(node)
127+
factory, count, *options = node.arguments
128+
129+
arguments = factory.source
130+
options = build_options_string(options)
131+
arguments += ", #{options}" unless options.empty?
132+
133+
replacement = format_receiver(node.receiver)
134+
replacement += format_method_call(node, 'create', arguments)
135+
"#{count.source}.times { #{replacement} }"
136+
end
115137
end
116138

117-
def factory_call_replacement(body, count)
118-
receiver, factory, options = *factory_call(body)
139+
# :nodoc:
140+
class CreateListCorrector < Corrector
141+
def initialize(node)
142+
@node = node.parent
143+
end
119144

120-
arguments = ":#{factory}, #{count}"
121-
options = build_options_string(options)
122-
arguments += ", #{options}" unless options.empty?
145+
def call(corrector)
146+
replacement = if node.body.block_type?
147+
call_with_block_replacement(node)
148+
else
149+
call_replacement(node)
150+
end
123151

124-
replacement = format_receiver(receiver)
125-
replacement += format_method_call(body, 'create_list', arguments)
126-
replacement
127-
end
152+
corrector.replace(node.loc.expression, replacement)
153+
end
128154

129-
def build_options_string(options)
130-
options.map(&:source).join(', ')
131-
end
155+
private
132156

133-
def format_method_call(node, method, arguments)
134-
if node.parenthesized?
135-
"#{method}(#{arguments})"
136-
else
137-
"#{method} #{arguments}"
157+
attr_reader :node
158+
159+
def call_with_block_replacement(node)
160+
block = node.body
161+
arguments = build_arguments(block, node.receiver.source)
162+
replacement = format_receiver(block.send_node.receiver)
163+
replacement += format_method_call(block, 'create_list', arguments)
164+
replacement += format_block(block)
165+
replacement
138166
end
139-
end
140167

141-
def format_receiver(receiver)
142-
return '' unless receiver
168+
def build_arguments(node, count)
169+
factory, *options = *node.send_node.arguments
170+
171+
arguments = ":#{factory.value}, #{count}"
172+
options = build_options_string(options)
173+
arguments += ", #{options}" unless options.empty?
174+
arguments
175+
end
176+
177+
def call_replacement(node)
178+
block = node.body
179+
factory, *options = *block.arguments
180+
181+
arguments = "#{factory.source}, #{node.receiver.source}"
182+
options = build_options_string(options)
183+
arguments += ", #{options}" unless options.empty?
143184

144-
"#{receiver.source}."
185+
replacement = format_receiver(block.receiver)
186+
replacement += format_method_call(block, 'create_list', arguments)
187+
replacement
188+
end
189+
190+
def format_block(node)
191+
if node.body.begin_type?
192+
format_multiline_block(node)
193+
else
194+
format_singeline_block(node)
195+
end
196+
end
197+
198+
def format_multiline_block(node)
199+
indent = ' ' * node.body.loc.column
200+
indent_end = ' ' * node.parent.loc.column
201+
" do #{node.arguments.source}\n" \
202+
"#{indent}#{node.body.source}\n" \
203+
"#{indent_end}end"
204+
end
205+
206+
def format_singeline_block(node)
207+
" { #{node.arguments.source} #{node.body.source} }"
208+
end
145209
end
146210
end
147211
end

spec/rubocop/cop/rspec/factory_bot/create_list_spec.rb

+30
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,36 @@
8888
include_examples 'autocorrect',
8989
'5.times { FactoryGirl.create :user }',
9090
'FactoryGirl.create_list :user, 5'
91+
92+
bad_code = <<-RUBY
93+
3.times do
94+
create(:user, :trait) { |user| create :account, user: user }
95+
end
96+
RUBY
97+
98+
good_code = <<-RUBY
99+
create_list(:user, 3, :trait) { |user| create :account, user: user }
100+
RUBY
101+
102+
include_examples 'autocorrect', bad_code, good_code
103+
104+
bad_code = <<-RUBY
105+
3.times do
106+
create(:user, :trait) do |user|
107+
create :account, user: user
108+
create :profile, user: user
109+
end
110+
end
111+
RUBY
112+
113+
good_code = <<-RUBY
114+
create_list(:user, 3, :trait) do |user|
115+
create :account, user: user
116+
create :profile, user: user
117+
end
118+
RUBY
119+
120+
include_examples 'autocorrect', bad_code, good_code
91121
end
92122

93123
context 'when EnforcedStyle is :n_times' do

0 commit comments

Comments
 (0)