Module: RSpecTracer::Storage::Backend Private

Defined in:
lib/rspec_tracer/storage/backend.rb

Overview

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Protocol every storage backend must satisfy. JsonBackend is the default; SqliteBackend is an opt-in alternative on MRI. The ‘spec/contracts/storage_backend.rb` shared-examples group asserts the full contract on every implementation.

Rationale for the method set (from ARCHITECTURE.md, Contracts between layers):

- `load_graph(schema_version:)` returns `Snapshot` or `nil`.
  `nil` means "no cache, or schema mismatch." Never raises on
  corruption - the backend's job is to normalize malformed
  inputs into nil + a log line.
- `save_graph(snapshot, schema_version:)` persists the graph.
  Either every file lands or none do (atomic via
  transactional_save).
- `last_run_id` returns the identifier of the most recent
  successful save, or `nil` if no cache exists.
- `transactional_save(&block)` runs the block with
  single-writer semantics and commits on clean exit; on any
  raise, the pre-block state is preserved.
- `clear!` removes everything the backend owns.

This module is intentionally documentation-only - it does not define stubs that raise NotImplementedError, because mutant would flag every ‘raise` as an alive mutation with no way to kill it. The shared-examples contract is the real gate.

Examples:

Registering a custom storage backend

class MyBackend
  def load_graph(schema_version:); end
  def save_graph(snapshot, schema_version:); end
  def last_run_id; end
  def transactional_save(&block); yield; end
  def clear!; end
end

RSpecTracer.configure do
  storage_backend MyBackend.new
end

Constant Summary collapse

REQUIRED_METHODS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Internal constant.

%i[
  load_graph
  save_graph
  last_run_id
  transactional_save
  clear!
].freeze

Class Method Summary collapse

Class Method Details

.build(cache_path:, configuration: RSpecTracer) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Construct the configured storage backend instance. Single source of truth for the json/sqlite dispatch + sqlite-gem- missing graceful fallback to :json, used by both Engine (runtime) and the CLI::CacheInfo / CLI::Explain sub-commands (post-run inspection). Pre-refactor, the dispatch lived only on Engine and the CLI sub-commands hardcoded the JsonBackend on-disk layout — so ‘bin/rspec-tracer cache:info` / `explain` reported “no last_run.json” even when `storage_backend :sqlite` had persisted a working cache.

Parameters:

  • cache_path (String)

    root cache directory.

  • configuration (Object) (defaults to: RSpecTracer)

    anything responding to the ‘storage_backend` / `storage_backend_opts` / `cache_retention_local_count` / `cache_size_warn_*` / `logger` accessors (defaults to the RSpecTracer top-level module).

Returns:



86
87
88
89
90
91
92
93
# File 'lib/rspec_tracer/storage/backend.rb', line 86

def self.build(cache_path:, configuration: RSpecTracer)
  case configuration.storage_backend
  when :sqlite
    build_sqlite(cache_path: cache_path, configuration: configuration)
  else
    build_json(cache_path: cache_path, configuration: configuration)
  end
end

.build_json(cache_path:, configuration:) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Internal helper for the tracer pipeline. JsonBackend / SqliteBackend constants are pre-required by every call site that loads RSpecTracer::Storage::Backend — Engine at gem-boot time, CLI sub-commands at their own require chain — so the method body stays focused on the construction shape mutant can actually verify. Returning a ‘require_relative` call to the method would be a structurally-unkillable mutation (Ruby caches requires; the test process always has both backends loaded).



105
106
107
108
109
110
111
112
113
114
# File 'lib/rspec_tracer/storage/backend.rb', line 105

def self.build_json(cache_path:, configuration:)
  JsonBackend.new(
    cache_path: cache_path,
    logger: configuration.logger,
    retention_local_count: configuration.cache_retention_local_count,
    warn_per_file_mb: configuration.cache_size_warn_per_file_mb,
    warn_total_mb: configuration.cache_size_warn_total_mb,
    serializer: configuration.storage_backend_opts[:serializer] || :json
  )
end

.build_sqlite(cache_path:, configuration:) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Internal helper for the tracer pipeline. See build_json for why the SqliteBackend require is at the call site rather than inside the method body.



120
121
122
123
124
125
126
127
# File 'lib/rspec_tracer/storage/backend.rb', line 120

def self.build_sqlite(cache_path:, configuration:)
  SqliteBackend.new(cache_path: cache_path, logger: configuration.logger)
rescue SqliteBackend::SqliteBackendError => e
  configuration.logger.warn(
    "rspec-tracer: sqlite backend unavailable (#{e.message}); falling back to :json"
  )
  build_json(cache_path: cache_path, configuration: configuration)
end

.conforms?(backend) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Verifies a candidate object satisfies the backend protocol. Used by Configuration#storage_backend to gate custom-backend registration at config-load time.

Parameters:

  • backend (Object)

    candidate backend instance

Returns:



63
64
65
# File 'lib/rspec_tracer/storage/backend.rb', line 63

def self.conforms?(backend)
  REQUIRED_METHODS.all? { |m| backend.respond_to?(m) }
end