module TestProf::FactoryProf

def config

def config
  @config ||= Configuration.new
end

def configure

def configure
  yield config
end

def flush_stack

def flush_stack
  return unless config.flamegraph?
  @stacks << @current_stack unless @current_stack.nil? || @current_stack.empty?
  @current_stack = []
end

def hash_template(name)

def hash_template(name)
  {
    name: name,
    total_count: 0,
    top_level_count: 0,
    total_time: 0.0,
    top_level_time: 0.0
  }
end

def init

Patch factory lib, init vars
def init
  @running = false
  log :info, "FactoryProf enabled (#{config.mode} mode)"
  patch!
end

def patch!

def patch!
  return if @patched
  FACTORY_BUILDERS.each(&:patch)
  @patched = true
end

def print(started_at)

def print(started_at)
  printer = config.printer
  printer.dump(result, start_time: started_at, threshold: config.threshold)
end

def reset!

def reset!
  @stacks = [] if config.flamegraph?
  @depth = 0
  @stats = Hash.new do |h, k|
    h[k] = hash_template(k)
    h[k][:variations] = Hash.new { |hh, variation_key| hh[variation_key] = hash_template(variation_key) }
    h[k]
  end
  flush_stack
end

def result

def result
  Result.new(@stacks, @stats)
end

def run

then runs
Inits FactoryProf and setups at exit hook,
def run
  init
  started_at = TestProf.now
  at_exit do
    print(started_at)
  end
  start
end

def running?

def running?
  @running == true
end

def start

def start
  reset!
  @running = true
end

def stop

def stop
  @running = false
end

def track(factory, variation:)

def track(factory, variation:)
  return yield unless running?
  @depth += 1
  @current_stack << factory if config.flamegraph?
  track_count(@stats[factory])
  track_count(@stats[factory][:variations][variation_name(variation)]) if config.include_variations?
  t1 = TestProf.now
  begin
    yield
  ensure
    t2 = TestProf.now
    track_time(@stats[factory], t1, t2)
    track_time(@stats[factory][:variations][variation_name(variation)], t1, t2) if config.include_variations?
    @depth -= 1
    flush_stack if @depth.zero?
  end
end

def track_count(factory)

def track_count(factory)
  factory[:total_count] += 1
  factory[:top_level_count] += 1 if @depth == 1
end

def track_time(factory, t1, t2)

def track_time(factory, t1, t2)
  elapsed = t2 - t1
  factory[:total_time] += elapsed
  factory[:top_level_time] += elapsed if @depth == 1
end

def variation_name(variation)

def variation_name(variation)
  return "-" if variation.empty?
  variations_count = variation.to_s.scan(/[\w]+/).size
  return "[...]" if variations_count > config.variations_limit
  variation
end