Module: StandardAudit::ReferencePreloading

Extended by:
ActiveSupport::Concern
Included in:
AuditLog
Defined in:
lib/standard_audit/reference_preloading.rb

Overview

Batch resolution of the GlobalID-backed actor / target / scope references on a page of audit rows, plus the per-row memo the readers consult.

Why this exists: AuditLog#actor resolves actor_gid through GlobalID::Locator.locate, which is one query per row. Rendering N rows that each read actor and target issues O(N) lookups — an N+1 that Prosopite fails in every consuming app. Before this concern existed, apps worked around it by defining their own preloaded_actor= writer (or reaching into @preloaded_actor with instance_variable_set) and hand-rolling a per-type whitelist loader.

Memo semantics

The memo is a Hash consulted with key?, not a pair of ivars checked with defined?. That distinction matters: a reference that was preloaded but whose record has since been deleted memoizes nil, and reads back as nil without falling through to a query. "Preloaded, and the answer is nothing" is therefore distinct from "not preloaded".

actor= / target= / scope= populate the memo too, since the writer already holds the record. reload clears it.

Batched writes

StandardAudit.batch { ... } flushes through insert_all! and never instantiates an AuditLog, so nothing in this concern runs on that path — neither the memo nor the writers. It is a no-op for batched writes by construction, not by accident.

Constant Summary collapse

REFERENCES =

The GlobalID-backed reference columns. scope is preloadable but not in the default refs: set, since most read surfaces render actor/target only.

%i[actor target scope].freeze
DEFAULT_REFS =
%i[actor target].freeze

Instance Method Summary collapse

Instance Method Details

#preloaded_referencesObject

The memo itself. Public because preload_references writes through it from the class side; treat it as gem-internal — preloaded_actor= and actor_preloaded? are the supported surface.



245
246
247
# File 'lib/standard_audit/reference_preloading.rb', line 245

def preloaded_references
  @preloaded_references ||= {}
end

#reference_preloaded?(ref) ⇒ Boolean

Returns:

  • (Boolean)


249
250
251
# File 'lib/standard_audit/reference_preloading.rb', line 249

def reference_preloaded?(ref)
  preloaded_references.key?(ref.to_sym)
end

#reloadObject

Drops the memo so the next read re-resolves from the (possibly changed) gid columns.



261
262
263
264
# File 'lib/standard_audit/reference_preloading.rb', line 261

def reload(...)
  @preloaded_references = nil
  super
end

#write_preloaded_reference(ref, record) ⇒ Object

See preloaded_references — gem-internal, but public so the class-level preloader can reach it.



255
256
257
# File 'lib/standard_audit/reference_preloading.rb', line 255

def write_preloaded_reference(ref, record)
  preloaded_references[ref.to_sym] = record
end