Class: Hecks::Adapters::Heki

Inherits:
Object
  • Object
show all
Includes:
Journal, Snapshot
Defined in:
lib/hecks/adapters/driven/heki.rb,
lib/hecks/adapters/driven/heki/journal.rb,
lib/hecks/adapters/driven/heki/snapshot.rb,
lib/hecks/adapters/driven/heki/saga_store.rb

Overview

The file-backed store: a compressed snapshot (heki/snapshot.rb) plus an append-only journal beside it (heki/journal.rb). What stays here is the repository surface — find/all/save/delete, and the entry append/project pair the persistence port drives.

Defined Under Namespace

Modules: Journal, Snapshot Classes: Malformed, SagaStore

Constant Summary collapse

MAGIC =
"HEKI"
HEADER_BYTES =
8

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Journal

#entries

Constructor Details

#initialize(aggregate:, settings: {}, root: nil) ⇒ Heki

Returns a new instance of Heki.



28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/hecks/adapters/driven/heki.rb', line 28

def initialize(aggregate:, settings: {}, root: nil)
  @aggregate = aggregate
  @path      = resolve_path(settings, root)
  @journal_path = "#{@path}.journal"
  @events    = []
  # THE OPTIONAL saga-persistence capability's own scoping (§2/§4)
  # — falls back to the aggregate's own name for a directly-
  # instantiated adapter (specs), same fallback shape Postgres's
  # own @domain already uses.
  @domain    = (settings[:domain] || settings["domain"] || aggregate.name).to_s

  FileUtils.mkdir_p(File.dirname(@path))
end

Instance Attribute Details

#aggregateObject (readonly)

Returns the value of attribute aggregate.



26
27
28
# File 'lib/hecks/adapters/driven/heki.rb', line 26

def aggregate
  @aggregate
end

#pathObject (readonly)

Returns the value of attribute path.



26
27
28
# File 'lib/hecks/adapters/driven/heki.rb', line 26

def path
  @path
end

Instance Method Details

#all(order_by: nil, direction: :asc) ⇒ Object



49
50
51
52
# File 'lib/hecks/adapters/driven/heki.rb', line 49

def all(order_by: nil, direction: :asc)
  records = store.sort_by { |id, _| id }.map { |id, record| instance(id, record) }
  InMemoryOrdering.ordered(records, aggregate: @aggregate, order_by: order_by, direction: direction)
end

#append(entry) ⇒ Object



67
68
69
70
71
72
73
# File 'lib/hecks/adapters/driven/heki.rb', line 67

def append(entry)
  @entry_mirrors = entry.mirrors
  append_entry(entry.operation, entry.id, entry.state)
  entry
ensure
  @entry_mirrors = nil
end

#countObject



54
# File 'lib/hecks/adapters/driven/heki.rb', line 54

def count = store.size

#delete(id) ⇒ Object



90
91
92
93
94
95
96
97
# File 'lib/hecks/adapters/driven/heki.rb', line 90

def delete(id)
  return false unless find(id)

  entry = Ports::Persistence::Entry.new(operation: "delete", id: id.to_s, state: nil)
  append(entry)
  project(entry)
  true
end

#delete_saga(process_manager:, correlation:) ⇒ Object



111
112
113
# File 'lib/hecks/adapters/driven/heki.rb', line 111

def delete_saga(process_manager:, correlation:)
  saga_store.delete_saga(@domain, process_manager.to_s, correlation.to_s)
end

#each_saga(&block) ⇒ Object



115
# File 'lib/hecks/adapters/driven/heki.rb', line 115

def each_saga(&block) = saga_store.each_saga(@domain, &block)

#eventsObject



101
# File 'lib/hecks/adapters/driven/heki.rb', line 101

def events = @events

#find(id) ⇒ Object



42
43
44
45
46
47
# File 'lib/hecks/adapters/driven/heki.rb', line 42

def find(id)
  record = store[id.to_s]
  return nil unless record

  instance(id.to_s, record)
end

#project(entry) ⇒ Object



75
76
77
78
79
80
81
# File 'lib/hecks/adapters/driven/heki.rb', line 75

def project(entry)
  current = store
  entry.save? ? current[entry.id] = entry.state.dup : current.delete(entry.id)
  write(current)
  @store = current
  entry
end

#query(specification, args = {}, context: {}) ⇒ Object

registry: context[:registry] — Memory's own query already threads this through; Heki's own never did, which made none_in_state? (Ports::Query::InMemory) unconditionally return true (its own graceful "no registry, no way to look the target up" default) for EVERY none_in_state where-clause against a Heki-backed aggregate — silently excluding nothing, always, no matter the actual target state.



63
64
65
# File 'lib/hecks/adapters/driven/heki.rb', line 63

def query(specification, args = {}, context: {})
  Ports::Query::InMemory.execute(all, specification, args, registry: context[:registry])
end

#record_event(event) ⇒ Object



99
# File 'lib/hecks/adapters/driven/heki.rb', line 99

def record_event(event) = @events << event

#save(instance) ⇒ Object



83
84
85
86
87
88
# File 'lib/hecks/adapters/driven/heki.rb', line 83

def save(instance)
  entry = Ports::Persistence::Entry.new(operation: "save", id: instance.id.to_s, state: instance.state.dup)
  append(entry)
  project(entry)
  instance
end

#save_saga(process_manager:, correlation:, state:, memory:) ⇒ Object

── the OPTIONAL saga-persistence capability (§2) — Heki's own shape (a sibling snapshot+journal file pair, SagaStore, heki/saga_store.rb) rather than a table in a store this adapter doesn't have.



107
108
109
# File 'lib/hecks/adapters/driven/heki.rb', line 107

def save_saga(process_manager:, correlation:, state:, memory:)
  saga_store.save_saga(@domain, process_manager.to_s, correlation.to_s, state.to_s, memory)
end