class FactoryBotCaching::FactoryCache

Attributes

build_class[R]
cachable_overrides[R]
cache[R]
common_associations[R]
factory_name[R]
uncommon_associations[R]

Public Class Methods

new(factory_name:) click to toggle source
# File lib/factory_bot_caching/factory_cache.rb, line 29
def initialize(factory_name:)
  @factory_name       = factory_name
  @build_class        = FactoryGirl.factory_by_name(factory_name).build_class
  @cache              = new_cache(@build_class)
  @cachable_overrides = []
  collect_uncachable_traits
end

Public Instance Methods

fetch(overrides:, traits:, &block) click to toggle source
# File lib/factory_bot_caching/factory_cache.rb, line 39
def fetch(overrides:, traits:, &block)
  key = { overrides: overrides, traits: traits}
  if should_cache?(key)
    cache.fetch(key, &block)
  else
    block.call
  end
end
reset() click to toggle source
# File lib/factory_bot_caching/factory_cache.rb, line 48
def reset
  @cache = new_cache(build_class)
end
reset_counter() click to toggle source
# File lib/factory_bot_caching/factory_cache.rb, line 52
def reset_counter
  cache.reset_counters
end

Private Instance Methods

cacheable_override?(override_sym) click to toggle source

Determine whether or not an overridden value in a factory call is cacheable. We use some caching and perform comparisons in order of probability which nets a 70x improvement in performance over previous versions of this validation.

For example:

FactoryGirl.create(:customer, name: 'John Doe', email: 'john.doe@example.test')
FactoryGirl.create(:customer, name: 'John Doe', address_id: 123)

In the above examples, `address_id` is a passed in association and should not be cached.

@param override_sym [Symbol] @return [Boolean] true if the overridden value is cacheable, false otherwise

# File lib/factory_bot_caching/factory_cache.rb, line 105
def cacheable_override?(override_sym)
  # Search in order of probability to reduce lookup times:
  return true  if cachable_overrides.include?(override_sym)
  return false if common_associations.include?(override_sym)
  return false if uncommon_associations.include?(override_sym)
  cachable_overrides << override_sym
  true
end
collect_uncachable_traits() click to toggle source

Collect a list of traits that are considered 'uncachable' if passed in the overrides list. We collect two lists - belongs_to associations, which have a high probability to be overridden in a factory call and should be compared against first, followed by uncommon_associations

# File lib/factory_bot_caching/factory_cache.rb, line 73
def collect_uncachable_traits
  return unless build_class < ActiveRecord::Base

  @common_associations = []
  @uncommon_associations  = []

  reflections = build_class.reflect_on_all_associations
  reflections.each do |reflection|
    if reflection.macro == :belongs_to
      @common_associations << reflection.name.to_sym
      # In rails land, some foreign keys are symbols, some strings; coerce them here:
      @common_associations << reflection.foreign_key.to_sym
    else
      @uncommon_associations << reflection.name.to_sym
    end
  end
end
new_cache(build_class) click to toggle source
# File lib/factory_bot_caching/factory_cache.rb, line 60
def new_cache(build_class)
  if FactoryBotCaching.configuration.custom_cache_key.nil?
    FactoryRecordCache.new(build_class: build_class)
  else
    CustomizedCache.new(
      build_class: build_class,
      cache_key_generator: FactoryBotCaching.configuration.custom_cache_key)
  end
end
should_cache?(key) click to toggle source

Skip caching for factories that are called with passed in associations, as it mutates the association and persisted record

# File lib/factory_bot_caching/factory_cache.rb, line 115
def should_cache?(key)
  return false unless build_class < ActiveRecord::Base

  key[:overrides].all? do |key, v|
    cacheable_override?(key.to_sym)
  end
end