Pass index to block for *_list methods

When using one of the `*_list` methods, allow the block to receive array
index for each object:

```ruby
posts = build_list(:post, 10, title: 'Post number') do |post, i|
  post.title = "#{post.title} #{i + 1}"
end

posts.first.title # => "Post number 1"
posts.last.title  # => "Post number 10"
```

A block that only takes in the object still works as before, as does
leaving off the block entirely.

Co-authored-by: Mike Countis <mike.countis@gmail.com>
This commit is contained in:
Emmanuel Gautier 2020-04-24 11:36:17 -04:00 committed by Daniel Colson
parent 0f4b404569
commit 0b53d28b4c
No known key found for this signature in database
GPG Key ID: 88A364BBE77B1353
4 changed files with 48 additions and 1 deletions

View File

@ -1018,6 +1018,14 @@ To set the attributes for each of the factories, you can pass in a hash as you n
twenty_year_olds = build_list(:user, 25, date_of_birth: 20.years.ago)
```
In order to set different attributes for each factory, these methods may be passed a block, with the factory and the index as parameters:
```ruby
twenty_somethings = build_list(:user, 10) do |user, i|
user.date_of_birth = (20 + i).years.ago
end
```
`build_stubbed_list` will give you fully stubbed out instances:
```ruby

View File

@ -11,6 +11,14 @@ module FactoryBot
define_pair_strategy_method
end
def self.with_index(block, index)
if block&.arity == 2
->(instance) { block.call(instance, index) }
else
block
end
end
private
def define_singular_strategy_method
@ -29,7 +37,10 @@ module FactoryBot
raise ArgumentError, "count missing for #{strategy_name}_list"
end
Array.new(amount) { send(strategy_name, name, *traits_and_overrides, &block) }
Array.new(amount) do |i|
block_with_index = StrategySyntaxMethodRegistrar.with_index(block, i)
send(strategy_name, name, *traits_and_overrides, &block_with_index)
end
end
end

View File

@ -51,4 +51,18 @@ describe "build multiple instances" do
end
end
end
context "with a block that receives both the object and an index" do
subject do
FactoryBot.build_list(:post, 20, title: "The Indexed Block") do |post, index|
post.position = index
end
end
it "correctly uses the set value" do
subject.each_with_index do |record, index|
expect(record.position).to eq index
end
end
end
end

View File

@ -52,6 +52,20 @@ describe "create multiple instances" do
end
end
context "with a block that receives both the object and an index" do
subject do
FactoryBot.create_list(:post, 20, title: "The Indexed Block") do |post, index|
post.position = index
end
end
it "uses the new values" do
subject.each_with_index do |record, index|
expect(record.position).to eq index
end
end
end
context "without the count" do
subject { FactoryBot.create_list(:post, title: "The Hunting of the Bear") }