class FactoryBotCaching::FactoryRecordCache

Constants

CacheEntry

Attributes

build_class[R]
cache[R]
enumerator_cache[R]

Public Class Methods

new(build_class:) click to toggle source
# File lib/factory_bot_caching/factory_record_cache.rb, line 32
def initialize(build_class:)
  @build_class = build_class
  @cache = Hash.new do |factory_hash, key|
    factory_hash[key] = []
  end
  reset_counters
end

Public Instance Methods

fetch(overrides:, traits:, &block) click to toggle source
# File lib/factory_bot_caching/factory_record_cache.rb, line 40
def fetch(overrides:, traits:, &block)
  key = {overrides: overrides, traits: traits}
  now        = Time.now
  enumerator = enumerator_for(key, at: now)

  enumerator.until_end do |entry|
    # Entries are sorted by created at, so we can break as soon as we see an entry created_at after now
    break if entry.created_at > now
    record = build_class.find_by(build_class.primary_key => entry.identifier)
    return record unless record.nil?
  end

  cache_new_record(key, &block)
end
reset_counters() click to toggle source
# File lib/factory_bot_caching/factory_record_cache.rb, line 55
def reset_counters
  @enumerator_cache = Hash.new do |enumerator_hash, key|
    enumerator_hash[key] = ImmutableIterator.new(cache[key])
  end
end

Private Instance Methods

cache_new_record(cache_key, &block) click to toggle source
# File lib/factory_bot_caching/factory_record_cache.rb, line 76
def cache_new_record(cache_key, &block)
  new_record = process_block(&block)
  primary_key = new_record.class.primary_key

  entry = CacheEntry.new(Time.now, new_record[primary_key])
  cached_records = cache[cache_key]

  insert_at_index = cached_records.find_index { |record| record.created_at > entry.created_at }
  if insert_at_index.nil?
    cached_records << entry
  else
    cached_records.insert(insert_at_index, entry)
  end

  new_record
end
enumerator_for(key, at:) click to toggle source
# File lib/factory_bot_caching/factory_record_cache.rb, line 65
def enumerator_for(key, at:)
  enumerator    = enumerator_cache[key]
  boundary_time = lookback_start_time(time: at)
  enumerator.fast_forward { |entry| entry.created_at > boundary_time }
  enumerator
end
lookback_start_time(time: Time.now) click to toggle source
# File lib/factory_bot_caching/factory_record_cache.rb, line 72
def lookback_start_time(time: Time.now)
  time - FactoryBotCaching.configuration.cache_timeout
end
process_block(&block) click to toggle source
# File lib/factory_bot_caching/factory_record_cache.rb, line 93
def process_block(&block)
  record = nil

  Thread.new do
    ActiveRecord::Base.connection_pool.with_connection do |conn|
      conn.execute("SET statement_timeout = '20s'")
      record = FactoryBotCaching.without_caching(&block)
    end
  end.join
  record
rescue => e
  # Append the backtrace of the current thread to the backtrace of the joined thread
  e.set_backtrace(e.backtrace + caller)
  raise e
end