Class: ActiveMutator::AcceptedLedger

Inherits:
Object
  • Object
show all
Defined in:
lib/active_mutator/accepted_ledger.rb

Overview

Committed, repo-root ledger of accepted (equivalent) survivors. Deliberately NOT inside .active_mutator/: that dir is gitignored and disposable, while acceptance decisions are durable team/CI state.

Entries whose file no longer exists are kept, not pruned: the file may still exist on another branch, so deletion is the user's call. The runner warns about them on every run instead (see #missing_file_entries).

Constant Summary collapse

FILENAME =
".active_mutator_accepted.json"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, entries) ⇒ AcceptedLedger

Returns a new instance of AcceptedLedger.



28
29
30
31
# File 'lib/active_mutator/accepted_ledger.rb', line 28

def initialize(path, entries)
  @path = path
  @entries = entries
end

Class Method Details

.from_hash(hash) ⇒ Object



21
22
23
24
25
26
# File 'lib/active_mutator/accepted_ledger.rb', line 21

def self.from_hash(hash)
  Fingerprint.new(file: hash.fetch("file"), subject: hash.fetch("subject"),
                  description: hash.fetch("description"),
                  original_snippet: hash.fetch("original_snippet"),
                  ordinal: hash.fetch("ordinal"))
end

.load(root) ⇒ Object



15
16
17
18
19
# File 'lib/active_mutator/accepted_ledger.rb', line 15

def self.load(root)
  path = File.join(root, FILENAME)
  entries = File.exist?(path) ? JSON.parse(File.read(path)) : []
  new(path, entries.map { |e| from_hash(e) })
end

Instance Method Details

#accept!(new_fingerprints, all_current_fingerprints, scanned_files:) ⇒ Object



53
54
55
56
57
58
# File 'lib/active_mutator/accepted_ledger.rb', line 53

def accept!(new_fingerprints, all_current_fingerprints, scanned_files:)
  stale = stale_entries(all_current_fingerprints, scanned_files: scanned_files).to_set
  @entries = (@entries + new_fingerprints).uniq.reject { |e| stale.include?(e) }
  AtomicFile.write(@path, JSON.pretty_generate(@entries.map(&:to_h)))
  nil
end

#accepted?(fingerprint) ⇒ Boolean

Returns:

  • (Boolean)


33
# File 'lib/active_mutator/accepted_ledger.rb', line 33

def accepted?(fingerprint) = @entries.include?(fingerprint)

#missing_file_entries(root) ⇒ Object

Missing is objective regardless of run scope: such entries can never appear in scanned_files, so without this they'd be immortal AND silent.



49
50
51
# File 'lib/active_mutator/accepted_ledger.rb', line 49

def missing_file_entries(root)
  @entries.reject { |e| File.exist?(File.join(root, e.file)) }
end

#stale_entries(all_current_fingerprints, scanned_files:) ⇒ Object

Entries outside the scanned files can't be judged by this run, so they are never stale here. scanned_files: nil means "no file was fully scanned" (subject-level filtering active) — union only, prune nothing. See #24: a scoped accept run once deleted every out-of-scope entry.



39
40
41
42
43
44
45
# File 'lib/active_mutator/accepted_ledger.rb', line 39

def stale_entries(all_current_fingerprints, scanned_files:)
  return [] if scanned_files.nil?

  current = all_current_fingerprints.to_set
  scanned = scanned_files.to_set
  @entries.reject { |e| current.include?(e) || !scanned.include?(e.file) }
end