Class: Hecks::Adapters::Memory

Inherits:
Object
  • Object
show all
Defined in:
lib/hecks/adapters/driven/memory.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

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

Returns a new instance of Memory.



20
21
22
23
24
25
# File 'lib/hecks/adapters/driven/memory.rb', line 20

def initialize(aggregate:, settings: {}, root: nil)
  @aggregate = aggregate
  @records   = {}
  @events    = []
  @entries   = []
end

Instance Attribute Details

#aggregateObject (readonly)

Returns the value of attribute aggregate.



18
19
20
# File 'lib/hecks/adapters/driven/memory.rb', line 18

def aggregate
  @aggregate
end

Class Method Details

.tenant_capable?Boolean

TENANT-CAPABLE TRIVIALLY — see Runtime::TenantCheck's own header for the full reasoning. @records is a plain instance variable; two Runtime.boot calls build two entirely separate Registry objects and, through them, two entirely separate Memory instances, so two tenant boots never share this adapter's state by construction — nothing here needs to know "tenant" exists.

Returns:

  • (Boolean)


15
# File 'lib/hecks/adapters/driven/memory.rb', line 15

def self.tenant_capable? = true

Instance Method Details

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



30
31
32
# File 'lib/hecks/adapters/driven/memory.rb', line 30

def all(order_by: nil, direction: :asc)
  InMemoryOrdering.ordered(@records.values, aggregate: @aggregate, order_by: order_by, direction: direction)
end

#append(entry) ⇒ Object



38
39
40
41
# File 'lib/hecks/adapters/driven/memory.rb', line 38

def append(entry)
  @entries << entry
  entry
end

#atomic_put(entry, insert_only: false) ⇒ Object

One in-memory critical section in the only thread touching this plain Hash: classify and replace without a preliminary repository lookup. Durable append and projection remain ordered exactly as ordinary save.



60
61
62
63
64
65
66
67
68
# File 'lib/hecks/adapters/driven/memory.rb', line 60

def atomic_put(entry, insert_only: false)
  exists = @records.key?(entry.id.to_s)
  return :conflicted if insert_only && exists

  status = exists ? :replaced : :inserted
  append(entry)
  project(entry)
  status
end

#countObject



28
# File 'lib/hecks/adapters/driven/memory.rb', line 28

def count    = @records.size

#delete(id) ⇒ Object



70
71
72
73
74
# File 'lib/hecks/adapters/driven/memory.rb', line 70

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

#entriesObject



80
# File 'lib/hecks/adapters/driven/memory.rb', line 80

def entries = @entries.dup

#eventsObject



78
# File 'lib/hecks/adapters/driven/memory.rb', line 78

def events = @events

#find(id) ⇒ Object



27
# File 'lib/hecks/adapters/driven/memory.rb', line 27

def find(id) = @records[id.to_s]

#persistence_capabilitiesObject



16
# File 'lib/hecks/adapters/driven/memory.rb', line 16

def persistence_capabilities = [:atomic_put]

#project(entry) ⇒ Object



43
44
45
46
47
48
49
# File 'lib/hecks/adapters/driven/memory.rb', line 43

def project(entry)
  if entry.save?
    @records[entry.id] = Runtime::Instance.new(aggregate: @aggregate, id: entry.id, state: entry.state.dup)
  else
    @records.delete(entry.id)
  end
end

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



34
35
36
# File 'lib/hecks/adapters/driven/memory.rb', line 34

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

#record_event(event) ⇒ Object



76
# File 'lib/hecks/adapters/driven/memory.rb', line 76

def record_event(event) = @events << event

#reset!Object

Every other driven adapter (Postgres/PostgresEra/Sqlite/D1) already implements this — Ports::Persistence::AppendOnly#reset! forwards to it and only raises when the wrapped adapter doesn't respond to reset! at all, which was always this adapter's own gap, not a deliberate omission (nothing about "in memory" implies "cannot be cleared"). Existing callers that fully re-Hecks.boot a domain per test case don't need this — they get a brand new Memory instance, with brand new empty @records/@events/ @entries, for free. This is for the other case: a caller that deliberately keeps ONE booted runtime across many cases (to skip load_domain's own per-boot parse/verify cost) and wants each case to start from the same clean slate Hecks.boot would have given it, without paying for a fresh boot to get there.



95
96
97
98
99
100
# File 'lib/hecks/adapters/driven/memory.rb', line 95

def reset!
  @records = {}
  @events  = []
  @entries = []
  self
end

#save(instance) ⇒ Object



51
52
53
54
55
# File 'lib/hecks/adapters/driven/memory.rb', line 51

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