aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activerecord/CHANGELOG.md6
-rw-r--r--activerecord/lib/active_record/relation/batches.rb13
-rw-r--r--activerecord/test/cases/batches_test.rb10
3 files changed, 25 insertions, 4 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index d14b5b27f8..4bc31457c0 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,9 @@
+* `find_in_batches` now returns an `Enumerator` that can calculate its size.
+
+ See also #13938.
+
+ *Marc-André Lafortune*
+
* Make sure transaction state gets reset after a commit operation on the record.
If a new transaction was open inside a callback, the record was loosing track
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 666cef80a9..a06da659cb 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -96,17 +96,22 @@ module ActiveRecord
# the batch sizes.
def find_in_batches(options = {})
options.assert_valid_keys(:start, :batch_size)
- return to_enum(:find_in_batches, options) unless block_given?
relation = self
+ start = options[:start]
+ batch_size = options[:batch_size] || 1000
+
+ unless block_given?
+ return to_enum(:find_in_batches, options) do
+ total = start ? where(table[primary_key].gteq(start)).size : size
+ (total - 1).div(batch_size) + 1
+ end
+ end
if logger && (arel.orders.present? || arel.taken.present?)
logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
end
- start = options[:start]
- batch_size = options[:batch_size] || 1000
-
relation = relation.reorder(batch_order).limit(batch_size)
records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index 8216d74cb3..4ee09a640e 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -191,4 +191,14 @@ class EachTest < ActiveRecord::TestCase
end
end
end
+
+ if Enumerator.method_defined? :size
+ def test_find_in_batches_should_return_a_sized_enumerator
+ assert_equal 11, Post.find_in_batches(:batch_size => 1).size
+ assert_equal 6, Post.find_in_batches(:batch_size => 2).size
+ assert_equal 4, Post.find_in_batches(:batch_size => 2, :start => 4).size
+ assert_equal 4, Post.find_in_batches(:batch_size => 3).size
+ assert_equal 1, Post.find_in_batches(:batch_size => 10_000).size
+ end
+ end
end