Skip to content

Commit

Permalink
Extract private method to implement batch on loaded relation
Browse files Browse the repository at this point in the history
Isolate the behavior so it is clear what in `in_batches` is reposible
for this logic.
  • Loading branch information
rafaelfranca committed Aug 4, 2023
1 parent cfa5857 commit 94efd65
Showing 1 changed file with 36 additions and 21 deletions.
57 changes: 36 additions & 21 deletions activerecord/lib/active_record/relation/batches.rb
Expand Up @@ -236,14 +236,14 @@ def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore:
#
# NOTE: By its nature, batch processing is subject to race conditions if
# other processes are modifying the database.
def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil, order: DEFAULT_ORDER, use_ranges: nil)
def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil, order: DEFAULT_ORDER, use_ranges: nil, &block)
relation = self

unless Array(order).all? { |ord| [:asc, :desc].include?(ord) }
raise ArgumentError, ":order must be :asc or :desc or an array consisting of :asc or :desc, got #{order.inspect}"
end

unless block_given?
unless block
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self, order: order, use_ranges: use_ranges)
end

Expand All @@ -258,24 +258,14 @@ def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore:
end

if self.loaded?
records = relation.to_a
if start || finish
records = records.filter { |record|
(start.nil? || record.id >= start) && (finish.nil? || record.id <= finish)
}
end

records = records.sort_by { |record| record.id }
if order == :desc
records.reverse!
end

(0...records.size).step(batch_limit).each do |start|
subrelation = relation.spawn
subrelation.load_records(records[start, batch_limit])
yield subrelation
end
return
return batch_on_loaded_relation(
relation: relation,
start: start,
finish: finish,
order: order,
batch_limit: batch_limit,
&block
)
end

batch_orders = build_batch_orders(order)
Expand Down Expand Up @@ -309,7 +299,7 @@ def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore:
primary_key_offset = ids.last
raise ArgumentError.new("Primary key not included in the custom select clause") unless primary_key_offset

yield yielded_relation
block.call yielded_relation

break if ids.length < batch_limit

Expand Down Expand Up @@ -391,5 +381,30 @@ def act_on_ignored_order(error_on_ignore)
def get_the_order_of_primary_key(order)
Array(primary_key).zip(Array(order))
end

def batch_on_loaded_relation(relation:, start:, finish:, order:, batch_limit:)
records = relation.to_a

if start || finish
records = records.filter do |record|
(start.nil? || record.id >= start) && (finish.nil? || record.id <= finish)
end
end

records = records.sort_by { |record| record.id }

if order == :desc
records.reverse!
end

(0...records.size).step(batch_limit).each do |start|
subrelation = relation.spawn
subrelation.load_records(records[start, batch_limit])

yield subrelation
end

nil
end
end
end

0 comments on commit 94efd65

Please sign in to comment.