module TestProf::AnyFixture

Make DB fixtures from blocks.

Constants

ANY_FIXTURE_IGNORE_RXP
ANY_FIXTURE_RXP
INSERT_RXP
MODIFY_RXP

Attributes

adapter[R]

Public Class Methods

cached(id) { || ... } click to toggle source
# File lib/test_prof/any_fixture.rb, line 127
def cached(id)
  cache.fetch(id) { yield }
end
clean() click to toggle source

Clean all affected tables (but do not reset cache)

# File lib/test_prof/any_fixture.rb, line 165
def clean
  disable_referential_integrity do
    tables_cache.keys.reverse_each do |table|
      ActiveRecord::Base.connection.execute %(
        DELETE FROM #{table}
      )
    end
  end
end
config() click to toggle source
# File lib/test_prof/any_fixture.rb, line 96
def config
  @config ||= Configuration.new
end
configure() { |config| ... } click to toggle source
# File lib/test_prof/any_fixture.rb, line 100
def configure
  yield config
end
register(id) { || ... } click to toggle source

Register a block of code as a fixture, returns the result of the block execution

# File lib/test_prof/any_fixture.rb, line 119
def register(id)
  cached(id) do
    ActiveSupport::Notifications.subscribed(method(:subscriber), "sql.active_record") do
      yield
    end
  end
end
register_dump(name, clean: true, **options) { || ... } click to toggle source

Create and register new SQL dump. Use `watch` to provide additional paths to watch for dump re-generation

# File lib/test_prof/any_fixture.rb, line 134
def register_dump(name, clean: true, **options)
  called_from = caller_locations(1, 1).first.path
  watch = options.delete(:watch) || [called_from]
  cache_key = options.delete(:cache_key)
  skip = options.delete(:skip_if)

  id = "sql/#{name}"

  register_method = clean ? :register : :cached

  public_send(register_method, id) do
    dump = Dump.new(name, watch: watch, cache_key: cache_key)

    unless dump.force?
      next if skip&.call(dump: dump)

      next dump.within_prepared_env(import: true, **options) { dump.load } if dump.exists?
    end

    subscriber = ActiveSupport::Notifications.subscribe("sql.active_record", dump.subscriber)
    res = dump.within_prepared_env(**options) { yield }

    dump.commit!

    res
  ensure
    ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
  end
end
report_stats() click to toggle source
# File lib/test_prof/any_fixture.rb, line 193
      def report_stats
        if cache.stats.empty?
          log :info, "AnyFixture has not been used"
          return
        end

        msgs = []

        msgs <<
          <<~MSG
            AnyFixture usage stats:
          MSG

        first_column = cache.stats.keys.map(&:size).max + 2

        msgs << format(
          "%#{first_column}s  %12s  %9s  %12s",
          "key", "build time", "hit count", "saved time"
        )

        msgs << ""

        total_spent = 0.0
        total_saved = 0.0
        total_miss = 0.0

        cache.stats.to_a.sort_by { |(_, v)| -v[:hit] }.each do |(key, stats)|
          total_spent += stats[:time]

          saved = stats[:time] * stats[:hit]

          total_saved += saved

          total_miss += stats[:time] if stats[:hit].zero?

          msgs << format(
            "%#{first_column}s  %12s  %9d  %12s",
            key, stats[:time].duration, stats[:hit],
            saved.duration
          )
        end

        msgs <<
          <<~MSG

            Total time spent: #{total_spent.duration}
            Total time saved: #{total_saved.duration}
            Total time wasted: #{total_miss.duration}
          MSG

        log :info, msgs.join("\n")
      end
reporting_enabled() click to toggle source
# File lib/test_prof/any_fixture.rb, line 110
def reporting_enabled
  warn "AnyFixture.reporting_enabled is deprecated and will be removed in 1.1. Use AnyFixture.config.reporting_enabled instead"
  config.reporting_enabled
end
Also aliased as: reporting_enabled?
reporting_enabled=(val) click to toggle source

Backward compatibility

# File lib/test_prof/any_fixture.rb, line 105
def reporting_enabled=(val)
  warn "AnyFixture.reporting_enabled is deprecated and will be removed in 1.1. Use AnyFixture.config.reporting_enabled instead"
  config.reporting_enabled = val
end
reporting_enabled?()
Alias for: reporting_enabled
reset() click to toggle source

Reset all information and clean tables

# File lib/test_prof/any_fixture.rb, line 176
def reset
  clean
  tables_cache.clear
  cache.clear
end
subscriber(_event, _start, _finish, _id, data) click to toggle source
# File lib/test_prof/any_fixture.rb, line 182
def subscriber(_event, _start, _finish, _id, data)
  matches = data.fetch(:sql).match(INSERT_RXP)
  return unless matches

  table_name = matches[1]

  return if /sqlite_sequence/.match?(table_name)

  tables_cache[table_name] = true
end

Private Class Methods

cache() click to toggle source
# File lib/test_prof/any_fixture.rb, line 248
def cache
  @cache ||= Cache.new
end
disable_referential_integrity() { || ... } click to toggle source
# File lib/test_prof/any_fixture.rb, line 256
def disable_referential_integrity
  connection = ActiveRecord::Base.connection
  return yield unless connection.respond_to?(:disable_referential_integrity)
  connection.disable_referential_integrity { yield }
end
tables_cache() click to toggle source
# File lib/test_prof/any_fixture.rb, line 252
def tables_cache
  @tables_cache ||= {}
end

Public Instance Methods

to_digest() click to toggle source
# File lib/test_prof/any_fixture/dump.rb, line 15
def to_digest
  to_s
end

Private Instance Methods

build_path(name, digest) click to toggle source
# File lib/test_prof/any_fixture/dump.rb, line 185
def build_path(name, digest)
  dir = TestProf.artifact_path(
    File.join(AnyFixture.config.dumps_dir)
  )

  FileUtils.mkdir_p(dir)

  File.join(dir, "#{name}-#{digest}.sql")
end
import_via_active_record() click to toggle source
# File lib/test_prof/any_fixture/dump.rb, line 175
def import_via_active_record
  conn = ActiveRecord::Base.connection

  File.open(path).each_line do |query|
    next if query.empty?

    conn.execute query
  end
end
run_after_callbacks(callback:, **options) click to toggle source
# File lib/test_prof/any_fixture/dump.rb, line 204
def run_after_callbacks(callback:, **options)
  # The order is vice versa to setup
  callback&.call(**options)
  adapter.teardown_env unless options[:import]
  AnyFixture.config.after_dump.each { |clbk| clbk.call(**options) }
end
run_before_callbacks(callback:, **options) click to toggle source
# File lib/test_prof/any_fixture/dump.rb, line 195
def run_before_callbacks(callback:, **options)
  # First, call config-defined setup callbacks
  AnyFixture.config.before_dump.each { |clbk| clbk.call(**options) }
  # Then, adapter-defined callbacks
  adapter.setup_env unless options[:import]
  # Finally, user-provided callback
  callback&.call(**options)
end