Class: Whodunit::Chronicles::Ledgers::SQLiteLedger

Inherits:
Whodunit::Chronicles::Ledger show all
Defined in:
lib/whodunit/chronicles/ledgers/sqlite_ledger.rb

Overview

SQLite-backed embedded durable ledger.

SQLiteLedger is the default solid local book. It can create its table, create indexes, report status, and append immutable ledger entries. The sqlite3 gem is loaded lazily only when a connection is not injected.

Constant Summary collapse

DEFAULT_TABLE =

Default SQLite table for entries.

'whodunit_chronicles_entries'

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Whodunit::Chronicles::Ledger

#migrate!, #partition_for, #verify

Constructor Details

#initialize(path:, table_name: DEFAULT_TABLE, connection: nil) ⇒ SQLiteLedger

Create a SQLite-backed ledger.

Parameters:

  • path (String)

    path to the SQLite database file

  • table_name (String) (defaults to: DEFAULT_TABLE)

    table receiving entries

  • connection (Object, nil) (defaults to: nil)

    optional SQLite-compatible connection



31
32
33
34
35
# File 'lib/whodunit/chronicles/ledgers/sqlite_ledger.rb', line 31

def initialize(path:, table_name: DEFAULT_TABLE, connection: nil)
  @path = path.to_s
  @table_name = table_name.to_s
  @connection = connection
end

Instance Attribute Details

#pathString (readonly)

Returns path to the SQLite database file.

Returns:

  • (String)

    path to the SQLite database file



21
22
23
# File 'lib/whodunit/chronicles/ledgers/sqlite_ledger.rb', line 21

def path
  @path
end

#table_nameString (readonly)

Returns table receiving entries.

Returns:

  • (String)

    table receiving entries



24
25
26
# File 'lib/whodunit/chronicles/ledgers/sqlite_ledger.rb', line 24

def table_name
  @table_name
end

Instance Method Details

#append(entry) ⇒ LedgerEntry

Append one ledger entry.

Parameters:

Returns:

Raises:

  • (AppendError)

    when SQLite rejects a duplicate event_id



75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/whodunit/chronicles/ledgers/sqlite_ledger.rb', line 75

def append(entry)
  connection.execute(<<~SQL, bind_values(entry))
    INSERT INTO #{quoted_table_name} (
      event_id, occurred_at, recorded_at, namespace, entity,
      identity, operation, actor, changes, metadata, payload
    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  SQL
  entry
rescue StandardError => e
  raise unless sqlite_constraint_error?(e)

  raise AppendError, "duplicate ledger event_id: #{entry.event_id}"
end

#ensure_indexes!SQLiteLedger

Create indexes useful for audit lookup and de-duplication.

Returns:



63
64
65
66
67
68
# File 'lib/whodunit/chronicles/ledgers/sqlite_ledger.rb', line 63

def ensure_indexes!
  connection.execute("CREATE UNIQUE INDEX IF NOT EXISTS #{index_name(:event_id)} ON #{quoted_table_name} (event_id)")
  connection.execute("CREATE INDEX IF NOT EXISTS #{index_name(:entity)} ON #{quoted_table_name} (namespace, entity)")
  connection.execute("CREATE INDEX IF NOT EXISTS #{index_name(:occurred_at)} ON #{quoted_table_name} (occurred_at)")
  self
end

#prepare!SQLiteLedger

Create the entries table if needed.

Returns:



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/whodunit/chronicles/ledgers/sqlite_ledger.rb', line 40

def prepare!
  connection.execute(<<~SQL)
    CREATE TABLE IF NOT EXISTS #{quoted_table_name} (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      event_id TEXT NOT NULL,
      occurred_at TEXT NOT NULL,
      recorded_at TEXT NOT NULL,
      namespace TEXT,
      entity TEXT,
      identity TEXT,
      operation TEXT NOT NULL,
      actor TEXT,
      changes TEXT,
      metadata TEXT,
      payload TEXT NOT NULL
    )
  SQL
  self
end

#statusHash<Symbol, Object>

Return lightweight operational status for this ledger.

Returns:

  • (Hash<Symbol, Object>)

    ledger status



92
93
94
95
96
97
98
99
100
# File 'lib/whodunit/chronicles/ledgers/sqlite_ledger.rb', line 92

def status
  {
    adapter: 'sqlite',
    path: path,
    table_name: table_name,
    prepared: prepared?,
    entries: prepared? ? count_entries : nil
  }
end