Documentation
¶
Overview ¶
Package pstore is a pure-Go (no cgo) reimplementation of the transaction engine at the heart of Ruby's PStore — MRI 4.0.5's transactional, Marshal-backed object store (the pstore-0.2.1 gem).
PStore persists a Hash (the "table") of Ruby objects to a file using Marshal. Reads and writes happen inside a transaction; on normal exit a read-write transaction commits the table back to the file (unless Abort was called), while a read-only transaction never writes. This package implements exactly that transactional, Marshal-(de)serialising core — independent of any interpreter — over two injected seams:
- a Backend (Load []byte / Store []byte): the bytes of the store file. The real File IO, O_CREAT, flock(LOCK_SH/LOCK_EX) and the atomic-rename / fast (rewind+truncate) save strategies are the host's job (rbgo wires real os.File + syscall.Flock); tests use an in-memory backend so the suite is fully deterministic and Ruby-free.
- the go-ruby-marshal codec, so the on-disk bytes are byte-compatible with a file written by MRI's PStore (Marshal.dump of the table Hash).
The table's keys and values are go-ruby-marshal Values (the same typed value model the rest of the go-embedded-ruby stack speaks), so a host binds its own Ruby objects to and from this model exactly as it does for Marshal itself.
What it is — and isn't ¶
The transaction state machine (load → run body → commit/abort, the read-only write guard, the not-in-transaction guard, the nested-transaction guard, the commit-only-on-change Marshal round-trip) is fully deterministic and needs no interpreter, so it lives here. The File open/flock/rename and the block's Ruby control flow (commit/abort exit the block via throw/catch in MRI) are the host's concern: this library exposes Commit and Abort as sentinel errors a host returns (or the body returns) to drive the same early-exit semantics.
Index ¶
- type Backend
- type Error
- type Store
- type Tx
- func (t *Tx) Abort() error
- func (t *Tx) Commit() error
- func (t *Tx) Delete(key marshal.Value) (marshal.Value, error)
- func (t *Tx) Fetch(key marshal.Value, def marshal.Value) (marshal.Value, error)
- func (t *Tx) Get(key marshal.Value) (marshal.Value, bool, error)
- func (t *Tx) RootQ(key marshal.Value) (bool, error)
- func (t *Tx) Roots() ([]marshal.Value, error)
- func (t *Tx) Set(key, val marshal.Value) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Backend ¶
type Backend interface {
// Load returns the current store bytes. An empty slice means a fresh store
// (MRI treats an empty / newly-created file as the empty table {}).
Load() ([]byte, error)
// Store overwrites the store bytes with the marshalled table.
Store(data []byte) error
}
Backend is the injected file seam: the raw bytes of the store. Load returns the current contents (empty for a not-yet-written store); Store overwrites them. The host implements this over a real os.File (with O_CREAT and flock); tests use an in-memory backend. PStore only reads on transaction entry and writes at most once, on a committing read-write transaction whose table actually changed.
type Error ¶
type Error struct {
// contains filtered or unexported fields
}
Error is the error type raised by every PStore operation that fails its transaction-state checks — the analogue of Ruby's PStore::Error. MRI raises it for "not in transaction", "in read-only transaction", "nested transaction", and "undefined key" (the no-default Fetch miss); the messages match.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is a PStore over an injected Backend. It holds no open file or lock; the host arranges flock around a transaction. A Store is single-transaction-at-a-time: it tracks whether a transaction is active to reproduce MRI's not-in-transaction and nested-transaction errors.
func (*Store) Transaction ¶
Transaction runs body as a PStore transaction. It loads the table from the backend (an empty backend yields the empty table), runs body, and — on a committing read-write transaction whose table changed — marshals the table back to the backend. body returns t.Commit() or t.Abort() to exit early, any other error to propagate it (no commit happens), or nil to finish normally (which commits a read-write transaction unless Abort was signalled).
readOnly true forbids writes: Set / Delete inside the body raise PStore::Error (Ruby's "in read-only transaction"), and the backend is never written.
Transaction is not reentrant: calling it from within a body raises PStore::Error ("nested transaction"), matching MRI's lock.try_lock guard.
type Tx ¶
type Tx struct {
// contains filtered or unexported fields
}
Tx is the handle passed to a transaction body. Its methods are the in-transaction PStore API; calling them is only legal while the body runs (the engine enforces this). The body returns nil to commit (read-write) or simply finish (read-only), or returns the result of t.Commit() / t.Abort() to exit early.
func (*Tx) Abort ¶
Abort signals an early abort and exits the body — PStore#abort. Return its result from the body: no changes are written and the body ends.
func (*Tx) Commit ¶
Commit signals an early commit and exits the body — PStore#commit. Return its result from the body: the transaction commits (read-write) and the body ends.
func (*Tx) Delete ¶
Delete removes key and returns its old value (or nil if absent) — PStore#delete. In a read-only transaction (or outside any transaction) it returns PStore::Error.
func (*Tx) Fetch ¶
Fetch returns the value for key, or def if key is absent. A nil def reproduces MRI's fetch(key) with no default: a miss raises PStore::Error ("undefined key"). To get a nil default value, pass marshal.Nil{}.
func (*Tx) Get ¶
Get returns the value for key, or (nil, false) if absent — PStore#[] (which returns nil for a missing key). Outside a transaction it returns PStore::Error.
func (*Tx) RootQ ¶
RootQ reports whether key exists — PStore#root? (alias of PStore#key?). Outside a transaction it returns PStore::Error.
